[NOIp2017提高组]逛公园

题目大意:
  给你一个有向图,若用dis(u,v)表示从u到v的最短路长度,求从1到n的长度不超过dis(1,n)+k的路径数。

思路:
  首先分别预处理出以1,n为起点的单、源最短路。
  对于合法的边重构图,然后拓扑排序判环,
  BFS时判断一下当前点是否在合法路径上,
  如果最后一个点没有被搜到且在合法路径上,那么肯定是一个0环。
  最后动态规划,f[i][j]表示长度为dis(1,i)+j的路径数量。

  1 #include<queue>
  2 #include<cstdio>
  3 #include<cctype>
  4 #include<vector>
  5 #include<cstring>
  6 #include<algorithm>
  7 #include<functional>
  8 #include<ext/pb_ds/priority_queue.hpp>
  9 inline int getint() {
 10     register char ch;
 11     while(!isdigit(ch=getchar()));
 12     register int x=ch^'0';
 13     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 14     return x;    
 15 }
 16 const int inf=0x7fffffff;
 17 const int N=100001,LIM=51;
 18 int n,m,lim,mod;
 19 struct Edge {
 20     int to,w;
 21 };
 22 std::vector<Edge> e0[N],e1[N];
 23 inline void add_edge(const int &u,const int &v,const int &w) {
 24     e0[u].push_back((Edge){v,w});
 25     e1[v].push_back((Edge){u,w});
 26 }
 27 struct Vertex {
 28     int id,dis;
 29     bool operator > (const Vertex &another) const {
 30         return dis>another.dis;
 31     }
 32 };
 33 int dis0[N],dis1[N];
 34 inline void dijkstra(const int &s,int dis[],const std::vector<Edge> e[]) {
 35     static __gnu_pbds::priority_queue<Vertex,std::greater<Vertex> > q;
 36     static __gnu_pbds::priority_queue<Vertex,std::greater<Vertex> >::point_iterator p[N];
 37     for(register int i=1;i<=n;i++) {
 38         p[i]=q.push((Vertex){i,dis[i]=i==s?0:inf});
 39     }
 40     while(!q.empty()&&q.top().dis!=inf) {
 41         const int x=q.top().id;
 42         q.pop();
 43         for(register unsigned i=0;i<e[x].size();i++) {
 44             const int &y=e[x][i].to,&w=e[x][i].w;
 45             if(dis[x]+w<dis[y]) {
 46                 q.modify(p[y],(Vertex){y,dis[y]=dis[x]+w});
 47             }
 48         }
 49     }
 50     while(!q.empty()) q.pop();
 51 }
 52 std::vector<int> top;
 53 inline bool check() {
 54     static int deg[N];
 55     static std::queue<int> q;
 56     memset(deg,0,sizeof deg);
 57     for(register int x=1;x<=n;x++) {
 58         for(register unsigned i=0;i<e0[x].size();i++) {
 59             const int &y=e0[x][i].to,&w=e0[x][i].w;
 60             if(dis0[x]+w==dis0[y]) deg[y]++;
 61         }
 62     }
 63     for(register int x=1;x<=n;x++) {
 64         if(!deg[x]) q.push(x);
 65     }
 66     while(!q.empty()) {
 67         const int x=q.front();
 68         q.pop();
 69         top.push_back(x);
 70         for(register unsigned i=0;i<e0[x].size();i++) {
 71             const int &y=e0[x][i].to,&w=e0[x][i].w;
 72             if(dis0[x]+w!=dis0[y]) continue;
 73             if(!--deg[y]) q.push(y);
 74         }
 75     }
 76     for(register int x=1;x<=n;x++) {
 77         if(deg[x]&&dis0[x]+dis1[x]<=dis0[n]+lim) return false;
 78     }
 79     return true;
 80 }
 81 inline int calc() {
 82     static int f[N][LIM];
 83     memset(f,0,sizeof f);
 84     f[1][0]=1;
 85     for(register int k=0;k<=lim;k++) {
 86         for(register unsigned i=0;i<top.size();i++) {
 87             const int &x=top[i];
 88             if(!f[x][k]) continue;
 89             for(register unsigned i=0;i<e0[x].size();i++) {
 90                 const int &y=e0[x][i].to,&w=e0[x][i].w;
 91                 if(dis0[x]+k+w<=dis0[y]+lim) {
 92                     (f[y][dis0[x]+k+w-dis0[y]]+=f[x][k])%=mod;
 93                 }
 94             }
 95         }
 96     }
 97     int ans=0;
 98     for(register int i=0;i<=lim;i++) {
 99         ans=(ans+f[n][i])%mod;
100     }
101     return ans;
102 }
103 inline void reset() {
104     for(register int i=1;i<=n;i++) {
105         e0[i].clear();
106         e1[i].clear();
107     }
108     top.clear();
109 }
110 int main() {
111     for(register int T=getint();T;T--) {
112         n=getint(),m=getint(),lim=getint(),mod=getint();
113         while(m--) {
114             const int u=getint(),v=getint(),w=getint();
115             add_edge(u,v,w);
116         }
117         dijkstra(1,dis0,e0);
118         dijkstra(n,dis1,e1);
119         printf("%d\n",check()?calc():-1);
120         reset();
121     }
122     return 0;
123 }

 

转载于:https://www.cnblogs.com/skylee03/p/7892282.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值