[NOIP2017]逛公园 最短路图 拓扑序DP

~~~题面~~~

题解:

  挺好的一道题。

  首先我们将所有边反向,跑出n到每个点的最短路,然后f[i][j]表示从i号节点出发,路径长比最短路大j的方案数。

  观察到,如果图中出现了0环,那么我们可以通过在环上走无数次来获得无数条不同路径,因此这就无解了。

  如果没有0环,当且仅当这张图的最短路图是一个DAG(可以画图思考一下),因为如果没有0环,而最短路图中出现了环,那么意味着你可以无数次以最短路到达同一个点,而不增加路径长,这显然是不可能的,同理,如果有0环,那么最短路图中就会出现环。

  因此我们判断不合法只需要对图进行一遍拓扑排序,如果不能将所有点都加入队列的话,就是出现了环,那么就输出-1.

  否则的话我们就按照拓扑序DP。

  从感性的角度上来说,,,我们需要先获取离终点近的DP值才能更新里终点远的DP值。所以要按拓扑序DP(具体证明之类的我也不会)

  DP的时候要先枚举比最短路长多少,因为DP时要通过原图转移,所以一个离终点近的点也可能会利用到离终点远的点,而DP的转移显然要依赖于用来更新其他点的值要 在被需要之前 更新完。

  所以先枚举点是不对的,因为这样没有明确的需要与被需要关系,也可以认为是在一个环上互相转移了。

  但是观察比最短路长多少这个条件,它是有明确的需要与被需要关系的,对于f[i][j]而言,j大的要利用j小的转移,j小的不可能用j大的转移,因为没有负边,所以这就避免了“环”的出现,于是可以保证DP转移合法。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 100100
  5 #define ac 202000
  6 #define LL long long
  7 
  8 int n, m, k, p, T;
  9 int in[AC], dis[AC];
 10 int q1[ac], head, tail;
 11 LL f[AC][60];
 12 bool z[AC];
 13 
 14 struct node{
 15     int dis, x;
 16 };
 17 
 18 struct cmp{
 19     bool operator() (node a, node b){
 20         return a.dis > b.dis;
 21     }
 22 };
 23 
 24 priority_queue<node, vector<node>, cmp> q;
 25 
 26 struct road{
 27     int Head[AC], date[ac], Next[ac], len[ac], tot;
 28     
 29     inline void init()
 30     {
 31         memset(Head, 0, sizeof(Head));
 32         tot = 0;
 33     }
 34     
 35     inline void add(int f, int w, int S){
 36         date[++tot] = w, Next[tot] = Head[f], Head[f] = tot, len[tot] = S;
 37     }    
 38 }E1, E2, E3;
 39 
 40 int read()
 41 {
 42     int x = 0;char c = getchar();
 43     while(c > '9' || c < '0') c = getchar();
 44     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 45     return x;
 46 }
 47 
 48 inline void up(LL &a, LL b)
 49 {
 50     a += b;
 51     if(a > p) a -= p;
 52 }
 53 
 54 void pre()
 55 {
 56     int a, b, c;
 57     E1.init(), E2.init(), E3.init();
 58     memset(f, 0, sizeof(f));
 59 //    memset(in, 0, sizeof(in));
 60     memset(z, 0, sizeof(z));
 61     head = tail = 0;
 62     n = read(), m = read(), k = read(), p = read();
 63     for(R i = 1; i <= m; i ++)
 64     {
 65         a = read(), b = read(), c = read();
 66         E1.add(b, a, c), E3.add(a, b, c);
 67     }
 68     memset(dis, 127, sizeof(dis));
 69     dis[n] = 0, q.push((node){0, n});
 70 }
 71 
 72 void spfa()
 73 {
 74     int x, now;
 75     while(!q.empty())
 76     {
 77         x = q.top().x, q.pop();
 78         while(z[x] && !q.empty()) x = q.top().x, q.pop();
 79         if(z[x]) break;
 80         z[x] = true;
 81         for(R i = E1.Head[x]; i; i = E1.Next[i])
 82         {
 83             now = E1.date[i];
 84             if(dis[now] > dis[x] + E1.len[i])
 85             {
 86                 dis[now] = dis[x] + E1.len[i];
 87                 q.push((node){dis[now], now});
 88             }    
 89         }
 90     }
 91 }
 92 
 93 void tsort()
 94 {
 95     int x, now;
 96     while(head < tail)
 97     {
 98         x = q1[++head];
 99         for(R i = E2.Head[x]; i; i = E2.Next[i])
100         {
101             now = E2.date[i], --in[now];
102             if(!in[now]) q1[++tail] = now;
103         }
104     }
105 }
106 
107 void build()
108 {
109     int now;
110     for(R i = 1; i <= n; i ++)
111     {
112         for(R j = E1.Head[i]; j; j = E1.Next[j])
113         {
114             now = E1.date[j];
115             if(dis[now] == dis[i] + E1.len[j])
116                 E2.add(i, now, E1.len[j]), ++ in[now];
117         }
118         //f[i][0] = 1; 
119     }
120     f[n][0] = 1;
121     for(R i = 1; i <= n; i ++) 
122         if(!in[i]) q1[++tail] = i;
123     tsort();
124 }
125 
126 void getans()
127 {
128     if(tail < n) 
129     {
130         memset(in, 0, sizeof(in));
131         printf("-1
"); 
132         return ;
133     }
134     int now, x;
135     for(R i = 0; i <= k; i ++)
136     {
137         for(R j = 1; j <= n; j ++)
138         {
139             x = q1[j];
140             for(R l = E3.Head[x]; l; l = E3.Next[l])
141             {
142                 now = E3.date[l];
143                 int t = E3.len[l] - dis[x] + dis[now];//获取现在新增的路径长度
144                 if(i - t >= 0) up(f[x][i], f[now][i - t]);
145             }
146         }
147     }
148     LL ans = 0;
149     for(R i = 0; i <= k; i ++) up(ans, f[1][i]);
150     printf("%lld
", ans);
151 }
152 
153 void work()
154 {
155     T = read();
156     while(T --)
157     {
158         pre();
159         spfa();
160         build();
161         getans();
162     }
163 }
164 
165 int main()
166 {
167     freopen("in.in", "r", stdin);
168     work();
169     fclose(stdin);
170     return 0;
171 }
View Code
原文地址:https://www.cnblogs.com/ww3113306/p/9802959.html