• 洛谷 P3953


    yet another NOIP2017(

    一年半以前写的做法被叉掉了/ll

    洛谷题目页面传送门

    给定一张有向图(G=(V,E),|V|=n,|E|=m),不包含重边与自环。求(1 o n)的长度不超过最短路加上(s)的路径个数,对给定模数取模。若有无限条,输出(-1)。本题多测。

    (ninleft[1,10^5 ight],minleft[1,2 imes10^5 ight],sin[1,50])。边权非负。测试组数不超过(5)

    先不考虑有边为(0)的情况。这种情况不难想到一个不是很难的DP。

    先Dijkstra算出(1 o i)的最短路径(dis_i)。那么不难发现一个很好的结论:在长度不超过(dis_n+s)的任意一条(1 o n)路上的任意一个点(x)处,都满足(1 o x)的长度不超过(dis_x+s)。因为如果超过了,即使(x o n)取最短路也不可能救得回来。

    数据范围提示我们std大概是(mathrm O(ns))(认为(n,m)同阶)的,则考虑设(dp_{i,j}(jin[0,s]))表示长度为(dis_i+j)(1 o i)路径个数。边界:(dp_{1,0}=1),目标:(sumlimits_{i=0}^sdp_{n,i}),转移方程:

    [dp_{i,j}=sum_{(k,j,len)in E}[dis_i+j-lenin[dis_k,dis_k+s]]dp_{k,dis_i+j-len-dis_k} ]

    至于DP顺序?反正无后效性就是了,你闲着无聊可以把所有状态拓扑排序一下,我用我的记忆化搜索(

    再考虑有边为(0)的情况,这时我们要考虑判无限条。一年半以前的naive想法是:有无限条当且仅当有(0)环。然后居然AC了?这里吐槽一下NOIP的数据质量。直到现在——有吏夜叉人。反例很简单,只需要构造出一个(0)环使得没有合法路径经过这个环上任意一个点即可。

    解决方案也不难。考虑对于每个环上的节点,都看是否有合法路径经过它即可。如何算是否有合法路径经过它?只需要看经过它的最短(1 o n)路径长度是否不超过(dis_n+s)。如何算经过它的最短(1 o n)路径长度?只需要结合之前Dijkstra求出来的(dis)数组,再Dijkstra算出从(n)出发经过反向边到达每个点的最短路径即可。如何算每个节点是否在环上?Tarjan求SCC即可,所在环大小超过(1)就在环上(因为无自环),DFS和拓扑排序都不再适用,因为它们只能判是否有环。

    时间复杂度(mathrm O!left(sum(nlog n+ns ) ight))。常数很大,但居然到了(2mathrm s)?这是我所没想到的(果然人傻常数大)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define mp make_pair
    #define X first
    #define Y second
    #define pb push_back
    const int N=100000,S=50;
    int n,m,s,mod;
    vector<pair<int,int> > nei[N+1],rnei[N+1];
    bool vis[N+1];
    int dis[N+1],dis0[N+1];
    void dijkstra(){//正图最短路 
    	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    	memset(dis,0x3f,sizeof(dis));memset(vis,0,sizeof(vis));
    	q.push(mp(dis[1]=0,1));
    	while(q.size()){
    		int x=q.top().Y;
    		q.pop();
    		if(vis[x])continue;
    		vis[x]=true;
    		for(int i=0;i<nei[x].size();i++){
    			int y=nei[x][i].X,len=nei[x][i].Y;
    			if(dis[x]+len<dis[y])q.push(mp(dis[y]=dis[x]+len,y));
    		}
    	}
    //	cout<<"dis=";for(int i=1;i<=n;i++)cout<<dis[i]<<" ";puts("");
    }
    void dijkstra0(){//反图最短路 
    	priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
    	memset(dis0,0x3f,sizeof(dis0));memset(vis,0,sizeof(vis));
    	q.push(mp(dis0[n]=0,n));
    	while(q.size()){
    		int x=q.top().Y;
    		q.pop();
    		if(vis[x])continue;
    		vis[x]=true;
    		for(int i=0;i<rnei[x].size();i++){
    			int y=rnei[x][i].X,len=rnei[x][i].Y;
    			if(dis0[x]+len<dis0[y])q.push(mp(dis0[y]=dis0[x]+len,y));
    		}
    	}
    }
    int dfn[N+1],low[N+1],nowdfn;
    int stk[N],top;
    bool ins[N+1];
    vector<vector<int> > scc;
    void dfs_tar(int x){
    	dfn[x]=low[x]=++nowdfn;
    	ins[stk[top++]=x]=true;
    	for(int i=0;i<nei[x].size();i++){
    		int y=nei[x][i].X,len=nei[x][i].Y;
    		if(len)continue;
    		if(!dfn[y])dfs_tar(y),low[x]=min(low[x],low[y]);
    		else if(ins[y])low[x]=min(low[x],dfn[y]);
    	}
    	if(dfn[x]==low[x]){
    		scc.pb(vector<int>());
    		while(true){
    			int y=stk[--top];
    			ins[y]=false;
    			scc.back().pb(y);
    			if(y==x)break;
    		}
    	}
    }
    int dp[N+1][S+1];
    int dfs(int x,int y){//记忆化搜索 
    	if(x==1&&y==0)return 1;//边界 
    	int &res=dp[x][y];
    	if(~res)return res;
    	res=0;
    	for(int i=0;i<rnei[x].size();i++){
    		int z=rnei[x][i].X,len=rnei[x][i].Y;
    		if(dis[z]<=dis[x]+y-len&&dis[x]+y-len<=dis[z]+s)(res+=dfs(z,dis[x]+y-len-dis[z]))%=mod;//转移 
    	}
    //	printf("dp[%d][%d]=%d
    ",x,y,res);
    	return res;
    }
    void mian(){
    	cin>>n>>m>>s>>mod;
    	for(int i=1;i<=n;i++)nei[i].clear(),rnei[i].clear();
    	while(m--){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		nei[x].pb(mp(y,z));rnei[y].pb(mp(x,z));
    	}
    	dijkstra();dijkstra0();
    	memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));nowdfn=0;top=0;memset(ins,0,sizeof(ins));scc.clear();
    	for(int i=1;i<=n;i++)if(!dfn[i])dfs_tar(i);//Tarjan求SCC 
    	for(int i=0;i<scc.size();i++){
    		vector<int> &v=scc[i];
    		if(v.size()==1)continue;
    		for(int j=0;j<v.size();j++)if(dis[v[j]]+dis0[v[j]]<=dis[n]+s)return puts("-1"),void();//判无限 
    	}
    	memset(dp,-1,sizeof(dp));
    	int ans=0;
    	for(int i=0;i<=s;i++)(ans+=dfs(n,i))%=mod;//目标 
    	cout<<ans<<"
    ";
    }
    int main(){
    	int testnum;
    	cin>>testnum;
    	while(testnum--)mian();
    	return 0;
    }
    
  • 相关阅读:
    Java Web Jsp EL ${ user.name }
    Java Web 网络留言板5 (javaBean技术)
    Java Web Web应用,打包和web.xml文件
    Java Web JSTL (实例)
    基于wpf的相关设计问题ViewModel
    c++ primer学习笔记(1)
    记上海俱乐部活动
    c++ primer学习笔记(3)字符串操作
    wpf&& silverlight的Behavior
    基于wpf的相关设计问题Command的使用
  • 原文地址:https://www.cnblogs.com/ycx-akioi/p/Luogu-P3953.html
Copyright © 2020-2023  润新知