1. 程式人生 > >NOIP2017 Day1 T3 逛公園(spfa+dfs)

NOIP2017 Day1 T3 逛公園(spfa+dfs)

原來k≤50。。我還擔心開不下。。

f[u][k]代表當前點在誤差小於等於k時到達終點的情況數量

首先求出n到每個點的最短路。然後dfs,每次如果dis[to]-dis[u]+edge[i].val≤k那麼就可以更新。如果搜過的點出現過那麼直接-1

最後返回dfs的值(也就是f[1][K])即可

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
int T,n,m,K,p;
struct node
{
	int to;
	int nxt;
	int val;
}edge[400005];
int head[100005];
int f[100005][52];
int used[100005];
int us[100005][52];
int dis[100005];
int cnt=1;
void init()
{
	memset(head,-1,sizeof(head));
	memset(f,0,sizeof(f));
	memset(us,0,sizeof(us));
	cnt=1;
}
void add(int from,int to,int val)
{
	edge[cnt].to=to;
	edge[cnt].val=val;
	edge[cnt].nxt=head[from];
	head[from]=cnt++;
}
queue<int>M;
void spfa()
{
	memset(dis,0x3f,sizeof(dis));
	memset(used,0,sizeof(used));
	dis[n]=0;
	used[n]=1;
	M.push(n);
	while(!M.empty())
	{
		int u=M.front();M.pop();
		for(int i=head[u];i!=-1;i=edge[i].nxt)
		{
			if(i%2==1)continue;
			int to=edge[i].to;
			if(dis[to]>dis[u]+edge[i].val)
			{
				dis[to]=dis[u]+edge[i].val;
				if(used[to])continue;
				used[to]=1;M.push(to);
			}
		}
		used[u]=0;
	}
}
int dfs(int u,int k)
{
	if(us[u][k])return -1;
	if(f[u][k])return f[u][k];
	us[u][k]=1;f[u][k]=(u==n?1:0);
	for(int i=head[u];i!=-1;i=edge[i].nxt)
	{
		if(i%2==0)continue;
		int to=edge[i].to,w,op;
		if((w=(k-(dis[to]+edge[i].val-dis[u])))>=0)
		{
			if((op=dfs(to,w))==-1)return /*f[u][k]*/=-1;
			else f[u][k]+=op,f[u][k]%=p;
		}
	}		
	return us[u][k]=0,f[u][k];
}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		init();
		scanf("%d%d%d%d",&n,&m,&K,&p);
		for(int i=1;i<=m;i++)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			add(a,b,c);
			add(b,a,c);
		}
		spfa();printf("%d\n",dfs(1,K));
	}
	return 0;
}