[NOIP2017]逛公園 最短路+拓撲排序+DP


[NOIP2017]逛公園

題目描述

策策同學特別喜歡逛公園。公園可以看成一張N個點M條邊構成的有向圖,且沒有 自環和重邊。其中1號點是公園的入口,N號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。

策策每天都會去逛公園,他總是從1號點進去,從N號點出來。

策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到N號點的最短路長為d,那么策策只會喜歡長度不超過d+K的路線。

策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎?

為避免輸出過大,答案對P取模。

如果有無窮多條合法的路線,請輸出−1。

輸入輸出格式

輸入格式:

第一行包含一個整數 T, 代表數據組數。

接下來T組數據,對於每組數據: 第一行包含四個整數 N,M,K,P,每兩個整數之間用一個空格隔開。

接下來M行,每行三個整數ai,bi,ci,代表編號為ai,bi的點之間有一條權值為ci的有向邊,每兩個整數之間用一個空格隔開。

輸出格式:

輸出文件包含 T 行,每行一個整數代表答案。

輸入輸出樣例

輸入樣例#1:
2
5 7 2 10
1 2 1
2 4 0
4 5 2
2 3 2
3 4 1
3 5 2
1 5 3
2 2 0 10
1 2 0
2 1 0
輸出樣例#1:
3
-1

說明

【樣例解釋1】

對於第一組數據,最短路為 3。 1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 為 3 條合法路徑。

【測試數據與約定】

對於不同的測試點,我們約定各種參數的規模不會超過如下

測試點編號   T    N    M    K    是否有0邊
1 5 5 10 0
2 5 1000 2000 0
3 5 1000 2000 50
4 5 1000 2000 50
5 5 1000 2000 50
6 5 1000 2000 50
7 5 100000 200000 0
8 3 100000 200000 50
9 3 100000 200000 50
10 3 100000 200000 50

對於 100%的數據,1P109,1ai,biN,0ci1000。

數據保證:至少存在一條合法的路線。

題解:本題的思路比較直接,沿着第一感覺一步一步做下去就可以了。

首先肯定要先跑最短路。然后呢?發現K只有50,所以我們一定是要從K入手。所以考慮DP,令f[i][j]表示走到i,多走的長度是j的方案數。(多走 指的是比最短路多的部分的長度)。

但是發現這個DP方程是存在環的,因為最短路徑圖上的邊以及零邊都是可以同行轉移的(從f[..][j]轉移到f[..][j]),所以怎么辦呢?拓撲排序唄!

我們將最短路徑圖上的邊以及零邊都拿出來跑拓撲排序,然后讓這些邊在轉移時必須沿着拓撲序轉移即可。特別地,如果一個零環位於一條從1到n長度<=d+K的路徑上,則輸出-1即可。

代碼有點丑了。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
#include <algorithm>
#include <utility>
#define mp(A,B) make_pair(A,B)
using namespace std;
const int maxn=100010;
const int maxm=200010;
int n,m,cnt,K,P,tot,ans;
int to[maxm],next[maxm],val[maxm],head[maxn],dis[maxn],vis[maxn],d[maxn],q[maxn<<1],f[51][maxn];
int to2[maxm],next2[maxm],val2[maxm],head2[maxn],dis2[maxn];
priority_queue<pair<int,int> > pq;
inline int rd()
{
	int ret=0;	char gc=getchar();
	while(gc<'0'||gc>'9')	gc=getchar();
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret;
}
inline void add(int a,int b,int c)
{
	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt;
	to2[cnt]=a,val2[cnt]=c,next2[cnt]=head2[b],head2[b]=cnt++;
}
inline void upd(int &x,int y)
{
	x=(x+y)%P;
}
void work()
{
	n=rd(),m=rd(),K=rd(),P=rd();
	int i,j,k,a,b,c,u;
	memset(head,-1,sizeof(head)),memset(head2,-1,sizeof(head2)),cnt=tot=ans=0;
	for(i=1;i<=m;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c);
	//1
	memset(vis,0,sizeof(vis)),memset(dis,0x3f,sizeof(dis)),memset(d,0,sizeof(d));
	pq.push(mp(0,1)),dis[1]=0;
	while(!pq.empty())
	{
		u=pq.top().second,pq.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(i=head[u];i!=-1;i=next[i])	if(dis[to[i]]>dis[u]+val[i])	dis[to[i]]=dis[u]+val[i],pq.push(mp(-dis[to[i]],to[i]));
	}
	//2
	memset(dis2,0x3f,sizeof(dis2)),memset(vis,0,sizeof(vis));
	pq.push(mp(0,n)),dis2[n]=0;
	while(!pq.empty())
	{
		u=pq.top().second,pq.pop();
		if(vis[u])	continue;
		vis[u]=1;
		for(i=head2[u];i!=-1;i=next2[i])	if(dis2[to2[i]]>dis2[u]+val2[i])	dis2[to2[i]]=dis2[u]+val2[i],pq.push(mp(-dis2[to2[i]],to2[i]));
	}
	//3
	for(i=1;i<=n;i++)	for(j=head[i];j!=-1;j=next[j])	if(dis[i]+val[j]==dis[to[j]])	d[to[j]]++;
	for(i=1;i<=n;i++)	if(!d[i])	q[++tot]=i;
	for(j=1;j<=tot;j++)
	{
		u=q[j];
		for(i=head[u];i!=-1;i=next[i])	if(dis[u]+val[i]==dis[to[i]])
		{
			d[to[i]]--;
			if(!d[to[i]])	q[++tot]=to[i];
		}
	}
	for(i=1;i<=n;i++)	if(d[i]&&dis[i]+dis2[i]<=dis[n]+K)
	{
		printf("-1\n");
		return ;
	}
	//DP
	memset(f,0,sizeof(f));
	f[0][1]=1;
	for(k=0;k<=K;k++)
	{
		for(i=1;i<=tot;i++)	for(u=q[i],j=head[u];j!=-1;j=next[j])	if(dis[u]+val[j]==dis[to[j]])
		{
			upd(f[k][to[j]],f[k][u]);
		}
		for(i=1;i<=n;i++)	for(j=head[i];j!=-1;j=next[j])	if(dis[i]+val[j]!=dis[to[j]]&&k+dis[i]+val[j]-dis[to[j]]<=K)
		{
			upd(f[k+dis[i]+val[j]-dis[to[j]]][to[j]],f[k][i]);
		}
	}
	for(i=0;i<=K;i++)	upd(ans,f[i][n]);
	printf("%d\n",ans);
}
int main()
{
	//freopen("park.in","r",stdin);
	//freopen("park.out","w",stdout);
	int T=rd();
	while(T--)	work();
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM