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

洛谷P3953 逛公園

超過 直接 事物 pla 最短 同時 return getchar() 長度

洛谷P3953 逛公園

題目描述

策策同學特別喜歡逛公園。公園可以看成一張\(N\)個點\(M\)條邊構成的有向圖,且沒有自環和重邊。其中1號點是公園的入口,\(N\)號點是公園的出口,每條邊有一個非負權值, 代表策策經過這條邊所要花的時間。
策策每天都會去逛公園,他總是從1號點進去,從\(N\)號點出來。
策策喜歡新鮮的事物,它不希望有兩天逛公園的路線完全一樣,同時策策還是一個 特別熱愛學習的好孩子,它不希望每天在逛公園這件事上花費太多的時間。如果1號點 到\(N\)號點的最短路長為\(d\),那麽策策只會喜歡長度不超過\(d+K\)的路線。
策策同學想知道總共有多少條滿足條件的路線,你能幫幫它嗎?
為避免輸出過大,答案對\(P\)

取模。
如果有無窮多條合法的路線,請輸出\(-1\)

輸入輸出格式

輸入格式:

第一行包含一個整數 \(T\), 代表數據組數。
接下來\(T\)組數據,對於每組數據: 第一行包含四個整數 \(N,M,K,P\),每兩個整數之間用一個空格隔開。
接下來\(M\)行,每行三個整數\(a_i,b_i,c_i\),代表編號為\(a_i,b_i\)?的點之間有一條權值為 \(c_i\)?的有向邊,每兩個整數之間用一個空格隔開。

輸出格式:

輸出文件包含 \(T\) 行,每行一個整數代表答案。

思路

記憶化搜索
首先從\(N\)開始反向求一遍最短路,這樣就得到了每一個點到\(N\)號節點的最短路徑長度,記\(Dis_i\)

\(i\)號節點到\(N\)號節點的最短路徑長度。
\(F_{i,j}\)表示第\(i\)號節點在路線長度為\(d+k-j\)時的路線條數,或者說是第\(i\)號節點還可以浪費\(j\)個單位距離時的路線條數。
那麽轉移方程如下:\[F_{i,j}=\sum_{k \in \{ k| (i,k) \in E \}}{F_{k,j+Dis_i-(Dis_k+W_{i,k})}}\]
因此,本問題也就可以用記憶化搜索求了
再考慮有無數解的情況,很顯然,當且僅當出現零環的時候回有無數解。
也就是說,我們只要在記憶化搜索之前找一下有沒有零環,有的話直接輸出\(-1\)沒有的話在做記憶化搜索
可以用tarjan縮點判斷零環
先將圖中所有長度大於\(0\)
的邊都去掉,然後跑tarjan,如果縮出來的強連通分量中有頂點數大於\(1\)的,那麽就說明有零環。

CODE

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
#define MAXN 100010
#define MAXM 200010
struct R{
    int id,dis;
    R(){}
    R(int id,int dis):id(id),dis(dis){}
    bool operator < (const R &a) const {
        return dis>a.dis;
    }
};
struct Node{
    int u,v,w;
    Node(){}
    Node(int u,int v,int w):u(u),v(v),w(w){};
}p[MAXM],revp[MAXM];
int head[MAXN],revhead[MAXN],Next[MAXM],revNext[MAXM],dis[MAXN],cnt[MAXN][55],dfn[MAXN],low[MAXN],color[MAXN];
bool vis[MAXN];
int i,j,k,m,n,x,y,u,v,w,tot,revtot,times,colnum,t,r,mod,mindis;
stack<int> mstack;
char readc;
bool flag;
void read(int &n){
    while((readc=getchar())<48||readc>57);
    n=readc-48;
    while((readc=getchar())>=48&&readc<=57) n=n*10+readc-48;
}
void addNode(int u,int v,int w){
    p[++tot]=Node(u,v,w);
    Next[tot]=head[u],head[u]=tot;
    revp[++revtot]=Node(v,u,w);
    revNext[revtot]=revhead[v],revhead[v]=revtot;
}
bool relax(int u,int v,int w){
    if(dis[u]+w<dis[v]){
        dis[v]=dis[u]+w;
        return true;
    }
    return false;
}
void tarjan(int src){
    low[src]=dfn[src]=++times;
    vis[src]=true;
    mstack.push(src);
    for(int i=head[src];i+1;i=Next[i]){
        if(p[i].w) continue;
        if(!dfn[p[i].v]){
            tarjan(p[i].v);
            low[src]=min(low[src],low[p[i].v]);
        }else{
            if(vis[p[i].v]) low[src]=min(low[src],dfn[p[i].v]);
        }
    }
    if(low[src]==dfn[src]){
        int tmp;
        colnum++;
        do{
            tmp=mstack.top(); mstack.pop();
            vis[tmp]=false;
            color[colnum]++;
        }while(tmp!=src);
    }
}
void dijkstra(int src){
    memset(dis,0x3f,sizeof(dis));
    priority_queue<R> mque;
    dis[src]=0;
    mque.push(R(src,0));
    while(!mque.empty()){
        R tmp=mque.top(); mque.pop();
        if(tmp.dis>dis[tmp.id]) continue;
        for(int i=revhead[tmp.id];i+1;i=revNext[i])
            if(relax(revp[i].u,revp[i].v,revp[i].w)){
                mque.push(R(revp[i].v,dis[revp[i].v]));
            }
    }
}
int dfs(int src,int cost){
    if(cnt[src][cost]+1) return cnt[src][cost];
    cnt[src][cost]=0;
    if(src==n) cnt[src][cost]++;
    for(int i=head[src];i+1;i=Next[i]){
        if((p[i].w+dis[p[i].v])-dis[src]<=cost){
            cnt[src][cost]+=dfs(p[i].v,cost+dis[src]-p[i].w-dis[p[i].v]);
            cnt[src][cost]%=mod;
        }
    }
    return cnt[src][cost];
}
int main(){
    read(t);
    for(r=1;r<=t;r++){
        memset(head,-1,sizeof(head));
        memset(revhead,-1,sizeof(revhead));
        memset(vis,0,sizeof(vis));
        memset(color,0,sizeof(color));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(cnt,-1,sizeof(cnt));
        tot=revtot=-1;
        times=colnum=0;
        read(n),read(m),read(k),read(mod);
        for(i=1;i<=m;i++){
            read(u),read(v),read(w);
            addNode(u,v,w);
        }
        for(i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        flag=false;
        for(i=1;i<=colnum;i++){
            if(color[i]>1){
                flag=true;
                break;
            }
        }
        if(flag){
            printf("-1\n");
            continue;
        }
        dijkstra(n);
        mindis=dis[1];
        printf("%d\n",dfs(1,k));
    }
    return 0;
}

洛谷P3953 逛公園