1. 程式人生 > >[洛谷P3953] 逛公園

[洛谷P3953] 逛公園

esp amp 自己 枚舉 bool 相同 tchar mat tor

洛谷題目鏈接:逛公園

題目復制過來有點炸格式就不貼了,看題目請戳上面的鏈接

題意: 給出一張\(n\)個點,\(m\)條邊的有向圖,設\(1\)\(n\)的最短路徑長度為\(d\),問\(1\)\(n\)路徑長度小於等於\(d+k\)的路徑有多少條.

題解: \(k=0\)的部分分就是一個最短路計數,如果不會的話可以先去看一下最短路計數.

我們先考慮不含\(0\)環的情況.首先我們可以求出從\(1\)為起點到其他點的最短路的長度,記為\(dist[i]\).最後對答案產生貢獻的狀態只有\(dist[n]+k\),所以我們可以記\(f[i][j]\)表示從\(1\)出發到\(i\)節點路徑長度為\(dist[i]+j\)

的方案數.

那麽這樣我們就可以寫出狀態轉移方程\((x->to)\):\[f[to][dist[x]+e[i].w-dist[to]+j] += f[x][j]\]

然後需要特別註意的是枚舉順序,我們在枚舉\(to\)的時候要先將\(x\)處理完,並且第二維的枚舉狀態要從小到大枚舉.處理好這些,就可以獲得\(70\)分了.

為什麽說是\(70\)分呢?因為我們這樣並不能判斷出無窮多條路徑的方案.但是我們可以發現,如果存在無窮多條路徑,那麽在\(1\)\(n\)的最短路上(或是在一條\(1->n\)的路徑且長度小於等於\(dist[n]+k\)上)有一個\(0\)環,那麽就存在無窮多條路徑,因為可以在這個\(0\)

環上不斷繞.

所以要判斷無窮多條路徑,就要先判斷是否存在一個\(0\)環在一條可以對答案產生貢獻的路徑上.這個可以用拓撲排序實現.

因為有可能存在\(0\)環,所以我們更新\(dp\)數組的時候不能再一邊遍歷圖,一遍更新數組了,這時候我們就需要對要更新的狀態排個序,也就是讓\(dist\)小的排在前面更新,但是又由於\(0\)邊的存在,我們需要讓一條邊的起點來更新終點,所以在多個點\(dist\)相同的時候,可以按照拓撲序來排.

細節比較多,可以自己多想想.

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+5;
const int M = 2e5+5;
const int K = 50+5;

int T, n, m, k, yyj, ans = 0, dist[N][2], vis[N][2], point[N], in[N], ord[N];
int last[N][2], last0[N], ecnt[2], ecnt0 = 0;
int f[N][K];

struct edge{
    int to, nex, w;
}e[M][2], e0[M];

struct node{
    int id, dis;
    bool operator < (const node &a) const{
        return dis > a.dis;
    }
};

int gi(){
    int res = 0, f = 1; char i = getchar();
    while(i < '0' || i > '9'){ if(i == '-') f = -1; i = getchar(); }
    while(i >= '0' && i <= '9') res = res*10+i-'0', i = getchar();
    return res*f;
}

void clear(){
    memset(last, 0, sizeof(last)), ans = ecnt[0] = ecnt[1] = 0;
    memset(last0, 0, sizeof(last0)), ecnt0 = 0;
    memset(in, 0, sizeof(in)), memset(vis, 0, sizeof(vis));
    memset(dist, 127/3, sizeof(dist));
    memset(ord, 0, sizeof(ord));
}

void add(int x, int y, int z, int t){
    e[++ecnt[t]][t].to = y, e[ecnt[t]][t].w = z, e[ecnt[t]][t].nex = last[x][t], last[x][t] = ecnt[t];
}

void add0(int x, int y){
    e0[++ecnt0].to = y, e0[ecnt0].nex = last0[x], last0[x] = ecnt0;
}

void dijkstra(int st, int t){
    priority_queue <node> q; q.push((node){ st, 0 });
    dist[st][t] = 0;
    while(!q.empty()){
        node x = q.top(); q.pop();
        if(vis[x.id][t]) continue; vis[x.id][t] = 1;
        for(int to, i = last[x.id][t]; i; i = e[i][t].nex){
            to = e[i][t].to;
            if(dist[to][t] > x.dis+e[i][t].w)
                dist[to][t] = x.dis+e[i][t].w, q.push((node){ to, dist[to][t] });
        }
    }
}

bool cmpdis(int a, int b){
    if(dist[a][0] == dist[b][0]) return ord[a] < ord[b];
    return dist[a][0] < dist[b][0];
}

bool topsort(){
    queue <int> q; int cnt = 0;
    for(int i = 1; i <= n; i++)
        if(in[i] == 0) q.push(i), ord[i] = ++cnt;
    while(!q.empty()){
        int x = q.front(); q.pop();
        for(int to, i = last0[x]; i; i = e0[i].nex){
            to = e0[i].to, in[to]--;
            if(in[to] == 0) q.push(to), ord[to] = ++cnt;
        }
    }
    for(int i = 1; i <= n; i++)
        if(in[i] && dist[i][0]+dist[i][1] <= dist[n][0]+k) return false;
    return true;
}

void solve(int st){
    memset(f, 0, sizeof(f)), f[st][0] = 1;
    for(int i = 1; i <= n; i++) point[i] = i;
    sort(point+1, point+n+1, cmpdis);
    for(int l = 0; l <= k; l++){
        for(int i = 1; i <= n; i++){
            int x = point[i];
            for(int j = last[x][0]; j; j = e[j][0].nex){
                int to = e[j][0].to;
                if(dist[x][0]+e[j][0].w+l <= dist[to][0]+k)
                    (f[to][dist[x][0]+e[j][0].w+l-dist[to][0]] += f[x][l]) %= yyj;
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    int x, y, z; T = gi();
    while(T--){
        clear(); n = gi(), m = gi(), k = gi(), yyj = gi();
        for(int i = 1; i <= m; i++){
            x = gi(), y = gi(), z = gi(), add(x, y, z, 0), add(y, x, z, 1);
            if(z == 0) add0(x, y), in[y]++;
        }
        dijkstra(1, 0), dijkstra(n, 1);
        if(!topsort()){ cout << -1 << endl; continue; }
        solve(1);
        for(int i = 0; i <= k; i++) (ans += f[n][i]) %= yyj;
        cout << ans << endl;
    }
    return 0;
}

[洛谷P3953] 逛公園