zoukankan      html  css  js  c++  java
  • 【NOIP 2017】逛公园

    Description

    策策同学特别喜欢逛公园。公园可以看成一张N个点M条边构成的有向图,且没有 自环和重边。其中1号点是公园的入口,N号点是公园的出口,每条边有一个非负权值, 代表策策经过这条边所要花的时间。
    策策每天都会去逛公园,他总是从1号点进去,从N号点出来。
    策策喜欢新鲜的事物,它不希望有两天逛公园的路线完全一样,同时策策还是一个 特别热爱学习的好孩子,它不希望每天在逛公园这件事上花费太多的时间。如果1号点 到N号点的最短路长为d,那么策策只会喜欢长度不超过d+K的路线。
    策策同学想知道总共有多少条满足条件的路线,你能帮帮它吗?
    为避免输出过大,答案对P取模。
    如果有无穷多条合法的路线,请输出−1。

    solution

    正解:拓扑序DP
    这题其实有两个拓扑序,一个是 (dis[i]) ,即1到 (i) 的最短路的长度,另外一个就是图本身的拓扑序了,我们单独拿出满足1到 任意一点(i) 最短路的边,然后做DP即可,状态设计为 (f[i][j]),表示到达点 (i),路径长度为 (dis[i]+j) 的方案数,然后枚举转移即可,判 (-1) 的方法很巧妙,因为边的长度为0,所以0环上的点的 (dis) 都满足拓扑序,也就是拓扑排序中会出现环,那么直接判掉即可,即如果存在某个0环上的一点 (i) 满足 (dis[S][i]+dis[i][T]<=dis[S][T]+K) 那么就有无穷多的方案数了

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=200005,inf=2e8;
    int f[2][N],mod,n,m,K,head[N],nxt[N<<1],to[N<<1],dis[N<<1],num=0;
    bool vis[N],imp[N];int Head[N];
    void link(int x,int y,int z){
    	nxt[++num]=head[x];to[num]=y;dis[num]=z;head[x]=num;}
    void Link(int x,int y,int z){
    	nxt[++num]=Head[x];to[num]=y;dis[num]=z;Head[x]=num;}
    
    queue<int>q;
    void priwork(bool t){
    	for(int i=1;i<=n;i++)vis[i]=0,f[t][i]=inf;
    	if(t==0)q.push(1),vis[1]=1,f[t][1]=0;
    	else q.push(n),vis[n]=1,f[t][n]=0;
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		for(int i=(t?Head[x]:head[x]);i;i=nxt[i]){
    			RG int u=to[i];
    			if(f[t][x]+dis[i]<f[t][u]){
    				f[t][u]=f[t][x]+dis[i];
    				if(!vis[u])vis[u]=1,q.push(u);
    			}
    		}
    		vis[x]=0;
    	}
    }
    
    int dp[N][55],d[N],sum=0,Q[N];
    void solve(){
    	for(int i=1;i<=n;i++)
    		for(int j=head[i];j;j=nxt[j])
    			if(f[0][i]+dis[j]==f[0][to[j]])d[to[j]]++;
    	for(int i=1;i<=n;i++)if(!d[i])Q[++sum]=i;
    	RG int t=0;int x,u;
    	while(t!=sum){
    		x=Q[++t];
    		for(int i=head[x];i;i=nxt[i]){
    			u=to[i];
    			if(f[0][x]+dis[i]==f[0][u]){
    				d[u]--;
    				if(!d[u])Q[++sum]=u;
    			}
    		}
    	}
    }
    
    void Clear(){
    	memset(dp,0,sizeof(dp));
    	for(RG int i=0;i<N;i++)Q[i]=d[i]=head[i]=Head[i]=imp[i]=0;
    	sum=0;num=0;
    }
    inline void add(RG int &x,int y){x+=y;if(x>=mod)x-=mod;}
    void work()
    {
    	Clear();
    	int x,y,z;
    	scanf("%d%d%d%d",&n,&m,&K,&mod);
    	for(int i=1;i<=m;i++){
    		scanf("%d%d%d",&x,&y,&z);
    		link(x,y,z);Link(y,x,z);
    	}
    	priwork(0);priwork(1);solve();
    	
    	for(int i=1;i<=n;i++)
    		if(d[i]>0 && f[0][i]+f[1][i]<=f[0][n]+K){
    			puts("-1");return ;
    		}
    
    	dp[1][0]=1;
    	for(int k=0;k<=K;k++){
    		for(int P=1;P<=sum;P++){
    			int i=Q[P];
    		   if(!dp[i][k])continue;
    			for(RG int j=head[i];j;j=nxt[j]){
    				x=to[j];
    				if(f[0][i]+dis[j]==f[0][x])
    					add(dp[x][k],dp[i][k]);
    			}
    		}
    		for(RG int i=1;i<=n;i++){
    			if(!dp[i][k])continue;
    			for(RG int j=head[i];j;j=nxt[j]){
    				x=to[j];
    				if(f[0][i]+dis[j]!=f[0][x]
    					&& f[0][i]+k+dis[j]-f[0][x]<=K)
    					add(dp[x][f[0][i]+k+dis[j]-f[0][x]],dp[i][k]);
    			}
    		}
    	}
    	int ans=0;
    	for(int i=0;i<=K;i++)add(ans,dp[n][i]);
    	printf("%d
    ",ans);
    }
    
    int main()
    {
    	freopen("park.in","r",stdin);
    	freopen("park.out","w",stdout);
    	int T;cin>>T;
    	while(T--)work();
    	return 0;
    }
    
    
    
  • 相关阅读:
    Kendo UI
    Docker
    jQuery DataTables && Django serializer
    MySQL ODBC for Linux
    mongoengine
    Python之多线程
    Python中的正则表达式
    通过恢复目录(Catalogue)进行PDB级别的PITR恢复
    执行PDB的PITR恢复失败的说明
    在PDB级别中如何切换或重建UNDO表空间
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7919542.html
Copyright © 2011-2022 走看看