1. 程式人生 > >NOIP-模擬試題之--逛公園

NOIP-模擬試題之--逛公園

2018 NOIP 全套資料下載

題解:

考場時想到了縮環,然並卵,就是沒想到拆點按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>kdiss−>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);
}
}


原文:https://blog.csdn.net/Cold_Chair/article/details/78569163