LOJ P3953 逛公园 NOIP dp 最短路 拓扑排序

https://www.luogu.org/problemnew/show/P3953

开o2过了不开o2re一个点。。。写法如题

顺便一提这道题在我校oj是a不了的因为我校土豆服务器速度奇慢1s时限

  1 // luogu-judger-enable-o2
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cmath>
  7 #include<queue>
  8 using namespace std;
  9 const int maxn=110000;
 10 int n,m,k,p;
 11 struct nod{
 12     int y,v,next;
 13 }e[2][maxn*2];
 14 int head[2][maxn]={};
 15 int dis[2][maxn]={};
 16 int d[maxn]={},zz[maxn]={};
 17 int mx,cnt=0,ans=0,tot=0;
 18 int f[60][maxn]={};
 19 bool vis[maxn]={};
 20 queue< int >q;
 21 void init(int x,int y,int v){
 22     e[0][++tot].y=y;e[0][tot].v=v;e[0][tot].next=head[0][x];head[0][x]=tot;
 23     e[1][tot].y=x;e[1][tot].v=v;e[1][tot].next=head[1][y];head[1][y]=tot;
 24 }
 25 void SPFA(int x,int z){
 26     for(int i=1;i<=n;i++)vis[i]=0;
 27     q.push(x);vis[x]=1;dis[z][x]=0;
 28     int y,v,v1;
 29     while(!q.empty()){
 30         x=q.front();q.pop();v=dis[z][x];
 31         for(int i=head[z][x];i;i=e[z][i].next){
 32             y=e[z][i].y;v1=e[z][i].v;
 33             if(dis[z][y]>v+v1){
 34                 dis[z][y]=v+v1;
 35                 if(!vis[y])q.push(y);
 36                 vis[y]=1;
 37             }
 38         }
 39         vis[x]=0;
 40     }
 41 }
 42 bool Topsort(){
 43     for(int i=1;i<=n;i++)d[i]=0;
 44     for(int i=1;i<=n;i++){
 45         for(int j=head[0][i];j;j=e[0][j].next){
 46             if(e[0][j].v+dis[0][i]==dis[0][e[0][j].y])d[e[0][j].y]++;//按照最短路连边
 47         }
 48     }cnt=0;
 49     for(int i=1;i<=n;i++)if(!d[i])zz[++cnt]=i;
 50     for(int i=1;i<=cnt;i++){
 51         for(int j=head[0][zz[i]];j;j=e[0][j].next){
 52             if(e[0][j].v+dis[0][zz[i]]==dis[0][e[0][j].y]){
 53                 d[e[0][j].y]--;
 54                 if(!d[e[0][j].y])zz[++cnt]=e[0][j].y;
 55             }
 56         }
 57     }
 58     for(int j=1;j<=n;j++){//如果有0边构成的环,那么这个环一定到最后也有d
 59         if(d[j]&&dis[0][j]+dis[1][j]<=k+dis[0][n])return 1;//如果环在合法路上就不用dp了有无数种方案
 60     }return 0;
 61 }
 62 void DP(){
 63     for(int i=0;i<=k;i++)
 64         for(int j=0;j<=n;j++)f[i][j]=0;
 65     ans=0;f[0][1]=1;
 66     int y,v,x;
 67     for(int i=0;i<=k;i++){
 68         for(int j=1;j<=cnt;j++){
 69             x=zz[j];if(dis[1][x]==mx)continue;
 70             for(int w=head[0][x];w;w=e[0][w].next){
 71                 y=e[0][w].y;v=e[0][w].v;
 72                 if(dis[0][x]+v==dis[0][y])f[i][y]=(f[i][y]+f[i][x])%p;//用拓扑序给f[k][i]汇总一下
 73             }
 74         }
 75         for(int j=1;j<=n;j++){//f[k][x]往下延伸
 76             x=j;if(dis[1][x]==mx)continue;
 77             for(int w=head[0][x];w;w=e[0][w].next){
 78                 y=e[0][w].y;v=e[0][w].v;
 79                 if(dis[0][x]+v!=dis[0][y]){
 80                     if(i+dis[0][x]+v-dis[0][y]<=k)
 81                         f[i+dis[0][x]+v-dis[0][y]][y]=(f[i+dis[0][x]+v-dis[0][y]][y]+f[i][x])%p;
 82                 }
 83             }
 84         }ans=(ans+f[i][n])%p;
 85     }
 86 }
 87 int main(){
 88     //freopen("now.in","r",stdin);
 89     int T;scanf("%d",&T);mx=(int)1e8;
 90     while(T-->0){
 91         scanf("%d%d%d%d",&n,&m,&k,&p);
 92         for(int i=0;i<=n;i++)dis[0][i]=dis[1][i]=mx;
 93         for(int i=0;i<=n;i++)head[0][i]=head[1][i]=0;tot=0;
 94         int x,y,v;
 95         for(int i=1;i<=m;i++){
 96             scanf("%d%d%d",&x,&y,&v);init(x,y,v);
 97         }
 98         SPFA(1,0);SPFA(n,1);//cout<<dis[1][1]<<dis[0][n]<<endl;
 99         if(dis[1][1]==mx)printf("0\n");
100         else{
101             if(Topsort()){
102                 printf("-1\n");
103             }
104             else{
105                 DP();
106                 printf("%d\n",ans);
107             }
108         }
109     }
110     return 0;
111 }
View Code

 

转载于:https://www.cnblogs.com/137shoebills/p/8680953.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值