1. 程式人生 > >【NOIP 2017 提高組】逛公園

【NOIP 2017 提高組】逛公園

【題目】

傳送門

題目描述:

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

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

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

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

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

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

輸入格式:

第一行包含一個整數 tt,代表資料組數。

接下來 tt 組資料,對於每組資料:

第一行包含四個整數 nnmmkkpp,每兩個整數之間用一個空格隔開。

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

輸出格式:

輸出檔案包含 tt 行,每行一個整數代表答案。

樣例資料:

輸入 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

輸出 3 -1

備註:

【輸入輸出樣例1說明】 對於第一組資料,最短路為 33151-512451-2-4-512351-2-3-533 條合法路徑。

【資料規模與約定】 對於不同的測試點,我們約定各種引數的規模不會超過如下 在這裡插入圖片描述 對於 100%100\% 的資料,1p1091≤p≤10^91ai,bin1≤a_i,b_i≤n0ci10000≤c_i≤1000。 資料保證:至少存在一條合法的路線。

【分析】

30  pts(k=0)30\; pts(k=0)

這部分的分應該是比較容易得到的吧

k=0k=0,轉換一下題目意思就是統計最短路個數,由於 k=0k=0 是沒有 00 邊的,所以還不用判斷是否有無窮中路線

不過要注意的是一定要有 visvis 陣列啊,不然會重複計算的,就錯了(被坑了無數次了)

70  pts(70\;pts(沒有 00))

這一部分分是用 dpdp 來做的

首先肯定還是要求最短路(這應該是廢話)

定義 dis[i]dis[i]11ii 的最短路,f[i][j]f[i][j] 為比 dis[i]dis[i]jj 的方案數

現在考慮怎麼轉移,假如現在 kk 可以到 ii,邊權為 ww,並且是從 f[k][x]f[k][x] 轉移到 f[i][j]f[i][j]

那麼容易得到 x+dis[k]+wdis[i]=jx+dis[k]+w-dis[i]=j,移項得 x=dis[i]dis[k]+jwx=dis[i]-dis[k]+j-w,那方程就是:

f[i][j]=f[k][dis[i]dis[k]+jw]f[i][j]=\sum f[k][dis[i]-dis[k]+j-w]

(當然如果發現 dis[i]dis[k]+jw<0dis[i]-dis[k]+j-w<0 了直接跳過就好了)

實際操作的時候用記憶化搜尋實現就可以了

100  pts100\;pts

現在考慮加上 00 邊該怎麼辦

其實也比較簡單,只用再記錄一個 vis[i][j]vis[i][j] 表示 f[i][j]f[i][j] 這個狀態出現過沒有

如果存在環,從 ii 開始搜尋的話(狀態是 f[i][j]f[i][j]),就會再次搜到 ii(注意是搜尋到 ii,不是回溯到 ii

那麼假設這個環上的其他點為 p1,p2,...,pxp_1,p_2,...,p_x,邊權為 w1,w2,...,wxw_1,w_2,...,w_x,假如回到 ii 的狀態依舊是 f[i][j]f[i][j],就說明 w1+w2+...+wx=0w_1+w_2+...+w_x=0disdis 會互相抵消掉),就是 00 環,這時就輸出 1-1

【程式碼】

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define K 55
#define N 100005
#define M 500005
using namespace std;
int n,m,k,p,t1,t2,flag;
int d[N],f[N][K],vis[N][K];
int first[N],v[M],w[M],nxt[M];
int First[N],V[M],W[M],Nxt[M];
priority_queue<pair<int,int> >q;
void add(int x,int y,int z)  {nxt[++t1]=first[x];first[x]=t1;v[t1]=y;w[t1]=z;}
void Add(int x,int y,int z)  {Nxt[++t2]=First[x];First[x]=t2;V[t2]=y;W[t2]=z;}
void init()
{
	t1=t2=flag=0;
	memset(f,-1,sizeof(f));
	memset(first,0,sizeof(first));
	memset(First,0,sizeof(First));
}
void dijkstra(int s)
{
	int x,y,i;
	memset(d,127/3,sizeof(d));
	q.push(make_pair(0,s));d[s]=0;
	while(!q.empty())
	{
		x=q.top().second;q.pop();
		for(i=first[x];i;i=nxt[i])
		{
			y=v[i];
			if(d[y]>d[x]+w[i])
			{
				d[y]=d[x]+w[i];
				q.push(make_pair(-d[y],y));
			}
		}
	}
}
int dfs(int now,int val)
{
	if(~f[now][val])  return f[now][val];
	int i,to,num;f[now][val]=0,vis[now][val]=1;
	for(i=First[now];i;i=Nxt[i])
	{
		to=V[i];
		num=d[now]-d[to]+val-W[i];
		if(num<0)  continue;
		if(vis[to][num])  flag=1;
		f[now][val]=(f[now][val]+dfs(to,num))%p;
	}
	vis[now][val]=0;
	return f[now][val];
}
int main()
{
	int x,y,z,i,T