[NOIP2017]逛公园

题目:洛谷P3953、Vijos P2030、UOJ#331。

题目大意:
给你\(N\)点\(M\)边的有向无权图,没有负权但有零权。求\(1\)到\(N\)的路径中长度不超过\(d+K\)的路径的条数\(\mod P\),无穷输出\(-1\)。
解题思路:
考场上我写的貌似接近正解的dp,只是先判了零环,然后传递值的时候比较暴力,疯狂优化常数后拿了70分。
正解也是dp。
设\(f_{i,j}\)表示到\(i\)号点,比最短路径多\(j\)的路径条数,\(d_i\)表示到\(i\)的最短路径长度。
则\(f_{i,j}=\sum f_{p,d_p-d_i-e+j} \)(\(e\)为\(p\)到\(i\)的一条边权长,且\({p,d_p-d_i-e+j}\geqslant 0\))
边界\(f_{0,0}=1\)(需要加一条\(0\)到\(1\)的零边并作为起点,否则样例的零环会挂)。
我们可以用记忆化搜索计算\(f\),当状态\({i,j}\)出现了两次,则一定出现了零环,退出输出\(-1\)即可。
时间复杂度貌似有点玄学,但还是过了(最坏\(O(NK^2)\)?)。

C++ Code:

#include<bits/stdc++.h>
#define M 200005
#define N 100005
inline int readint(){
	int c=getchar(),d=0;
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^'0');
	return d;
}
struct edge{
	int to,dis,nxt;
}e[M],e2[M];
int head[N],head2[N],n,m,k,p,cnt1,cnt2,dis[N],l,r,q[130005];
int dp[N][52];
bool vis[N],ur[N][52],ok;
inline int add1(const int u,const int v,const int t){
	e[++cnt1]=(edge){v,t,head[u]};
	head[u]=cnt1;
}
inline int add2(const int u,const int v,const int t){
	e2[++cnt2]=(edge){u,t,head2[v]};
	head2[v]=cnt2;
}
int dfs(int u,int k){
	if(~dp[u][k])return dp[u][k];
	dp[u][k]=0,ur[u][k]=1;
	for(int i=head2[u];i!=-1&&ok;i=e2[i].nxt){
		int v=e2[i].to;
		int d=dis[u]-dis[v]-e2[i].dis+k;
		if(d<0)continue;
		if(ur[v][d]){
			ok=0;return 0;
		}
		dp[u][k]=(dp[u][k]+dfs(v,d))%p;
	}
	ur[u][k]=0;
	return dp[u][k];
}
int main(){
	for(int T=readint();T--;){
		memset(head,-1,sizeof head);
		memset(head2,-1,sizeof head2);
		memset(e,0,sizeof e);
		memset(e2,0,sizeof e2);
		cnt1=cnt2=0;
		n=readint(),m=readint(),k=readint(),p=readint();
		while(m--){
			int u=readint(),v=readint(),d=readint();
			add1(u,v,d);
			add2(u,v,d);
		}
		add1(0,1,0),add2(0,1,0);
		memset(dis,0x3f,sizeof dis);
		memset(vis,0,sizeof vis);
		dis[0]=0;
		l=q[1]=0,r=vis[0]=1;int u;
		while(l!=r){
			vis[u=q[l=l%130000+1]]=0;
			for(int i=head[u];i!=-1;i=e[i].nxt)
			if(dis[e[i].to]>dis[u]+e[i].dis){
				dis[e[i].to]=dis[u]+e[i].dis;
				if(!vis[e[i].to])vis[q[r=r%130000+1]=e[i].to]=1;
			}
		}
		memset(dp,-1,sizeof dp);
		memset(ur,0,sizeof ur);
		ok=dp[0][0]=1;
		int ans=0;
		for(int i=0;i<=k&&ok;++i)ans=(ans+dfs(n,i))%p;
		printf("%d\n",ok?ans:-1);
	}
	return 0;
}

 

posted @ 2018-06-01 17:53  Mrsrz  阅读(236)  评论(0编辑  收藏  举报