• [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
    ",ok?ans:-1);
    	}
    	return 0;
    }
    
  • 相关阅读:
    面向对象六
    面向对象五
    面向对象四
    面向对象三
    面向对象二
    CentOS7下安装Redis4.0
    在亚马逊的EC2环境中创建swap
    centos7安装rabbitmq操作步骤
    在VUE下使用阿里图标
    Centos7-安装telnet服务
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/9122787.html
Copyright © 2020-2023  润新知