让你求从1到n的路径中,长度和最短路径不超过k有多少条?
Warning:CCF老爷机严重卡常。
考场时想到了缩环,然并卵,就是没想到拆点按Turpo序dp。
70分的做法,先跑SPFA求最短路,然后再求最短路的条数,如果此时有0环,那么队列会无限大,可以超过10^6就退出,之后再强行拆点连边SPFA跑方案数。
发现如果原图中没有0环的话,拆点后的图是没有环的,所以强行Turpo一发,按顺序统计方案数即可。
这也启发出了判0环的另一种方法,即直接拆点Turpo,如果终点不能Turpo出来,则有0环在路径上。
千万不要以为这样就能过了,这个得跑6s。
卡常1:
正反两遍SPFA求出每个点到起点终点的距离,如果有拆点后的f[i][j]表示第i个点,现在比最短路的多了j的长度,若 diss−>i+j−diss−>t+disi−>t>k ,那么f[i][j]这个点就肯定无用了。
卡常2:
直接在最短路图上做Turpo,之后枚举层数,再按Turpo序枚举点。
这样做Turpo时快了许多,且之后算答案时如果一个点得方案数为0,可以直接跳过它。
这两个优化加上后估计老爷机跑2s。
Code:
#include
#include
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 1e5 + 5, M = 2e5 + 5;
int T, n, m, k, mo, x, y, z, bzans;
int next[M], to[M], cost[M], final[N], tot;
int bz[N], bx[N], d[M * 100];
int r[N], d2[N], f[N][51], dis[N], dis2[N];
int next2[M], to2[M], final2[N], tot2;
int b[M][3];
void read(int &x) {
char c = ' '; for(; c < '0' || c > '9'; c = getchar());
x = 0; for(; c >= '0' && c <= '9'; c = getchar()) x = x * 10 + c - 48;
}
void SPFA2(int *dis) {
fo(i, 1, n) dis[i] = 2e9;
d[1] = n; bz[n] = 1; dis[n] = 0;
for(int st = 1, en = 1; st <= en; st ++) {
int x = d[st];
for(int i = final[x]; i; i = next[i]) {
int y = to[i], z = cost[i];
if(dis[x] + z < dis[y]) {
dis[y] = dis[x] + z;
if(!bz[y]) bz[y] = 1, d[++ en] = y;
}
}
bz[x] = 0;
}
}
void SPFA() {
memset(dis, 127, sizeof dis);
d[1] = 1; bz[1] = 1; dis[1] = 0;
for(int st = 1, en = 1; st <= en; st ++) {
int x = d[st];
for(int i = final[x]; i; i = next[i]) {
int y = to[i], z = cost[i];
if(dis[x] + z < dis[y]) {
dis[y] = dis[x] + z;
if(!bz[y]) bz[y] = 1, d[++ en] = y;
}
}
bz[x] = 0;
}
}
void Link() {
fo(i, 1, tot2) next2[i] = 0;
fo(i, 1, n) final2[i] = 0;
tot2 = 0;
fo(x, 1, n) for(int i = final[x]; i; i = next[i]) {
int y = to[i];
if(dis[x] + cost[i] == dis[y])
next2[++ tot2] = final2[x], to2[tot2] = y, final2[x] = tot2;
}
}
int d0;
void Turpo() {
fo(i, 1, n) r[i] = 0;
fo(i, 1, n) for(int j = final2[i]; j; j = next2[j]) r[to2[j]] ++;
int st = 1, en = 0;
fo(i, 1, n) if(!r[i]) d[++ en] = i;
for(; st <= en; st ++) {
int x = d[st];
bz[x] = 1;
for(int i = final2[x]; i; i = next2[i])
if(!(-- r[to2[i]])) d[++ en] = to2[i];
}
bzans = 1;
fo(i, 1, n) if(dis[i] < 1e9 && dis2[i] < 1e9 && !bz[i])
bzans = 0;
memset(bz, 0, sizeof bz);
d0 = en;
}
int main() {
freopen("park.in", "r", stdin);
freopen("park.out", "w", stdout);
for(scanf("%d", &T); T; T --) {
fo(i, 1, n) final[i] = 0;
fo(i, 1, tot) next[i] = 0;
tot = 0;
scanf("%d %d %d %d", &n, &m, &k, &mo);
fo(i, 1, m) {
read(x); read(y); read(z);
b[i][0] = x; b[i][1] = y; b[i][2] = z;
next[++ tot] = final[y], to[tot] = x, cost[tot] = z, final[y] = tot;
}
SPFA2(dis2);
fo(i, 1, n) final[i] = 0;
fo(i, 1, tot) next[i] = 0;
tot = 0;
fo(i, 1, m) {
x = b[i][0], y = b[i][1], z = b[i][2];
next[++ tot] = final[x], to[tot] = y, cost[tot] = z, final[x] = tot;
}
SPFA();
Link();
Turpo();
if(!bzans) {
printf("-1\n");
continue;
}
memset(f, 0, sizeof f);
f[1][0] = 1;
fo(j, 0, k) fo(i, 1, d0) {
int x = d[i];
if(!f[x][j]) continue;
if(j + dis2[x] + dis[x] - dis[n] > k) continue;
for(int p = final[x]; p; p = next[p]) {
int y = to[p], k2 = j + dis[x] + cost[p] - dis[y];
if(k2 > k) continue;
f[y][k2] = (f[y][k2] + f[x][j]) % mo;
}
}
int ans = 0;
fo(j, 0, k) ans = (ans + f[n][j]) % mo;
printf("%d\n", ans);
}
}