博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【NOIP 2017】逛公园
阅读量:4563 次
发布时间:2019-06-08

本文共 2424 字,大约阅读时间需要 8 分钟。

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 
#include
#include
#include
#include
#include
#include
#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
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]
=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\n",ans);}int main(){ freopen("park.in","r",stdin); freopen("park.out","w",stdout); int T;cin>>T; while(T--)work(); return 0;}

转载于:https://www.cnblogs.com/Yuzao/p/7919542.html

你可能感兴趣的文章
Kafka序列化和反序列化与示例
查看>>
win10下VS2010中文输入法切换为英文卡死
查看>>
retinex相关代码汇总
查看>>
Cortex-M3 异常返回值EXC_RETURN
查看>>
kettle 转换字段遇到问题(couldn't get row from result set)——摘
查看>>
nginx首页根据IP跳转
查看>>
【2019-08-20】有点目标,有点计划,有点目的
查看>>
【2019-09-10】美,真的跟年龄无关
查看>>
【2019-09-28】少,但更好
查看>>
【2019-09-13】耐心观察是一种技能
查看>>
mysql数据库2-常用命令
查看>>
安卓开发环境搭建(转)
查看>>
Harris角点检测
查看>>
Struts2的处理流程及为Action的属性注入值
查看>>
设计中最常用的CSS选择器
查看>>
Maven项目打包成可执行Jar文件
查看>>
nginx http proxy 正向代理
查看>>
对BFC的总结
查看>>
23醒
查看>>
win7每天出现taskeng.exe进程的解决方案
查看>>