[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\n"); 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\n", 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 }