[Noip2017]逛公園


其實真的是一道思博題啊。為啥考試我就沒想出來??

因為長得太拓撲了(其他正解好像也確實是拓撲),然后顧忌到環就沒想。太菜了還是。

后來上課閑的沒事一想,靠,記憶化搜索啊。這樣一定是所有父親都計算完不重不漏。

(正着想不也是嘛)

很難受,要不然能多點分。


 

【問題描述】

策策同學特別喜歡逛公園。 公園可以看成一張$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$行,每行一個整數代表答案。

對於第一組數據,最短路為 3。

1 – 5, 1 – 2 – 4 – 5, 1 – 2 – 3 – 5 為 3 條合法路徑。


 

用$dp[u][k]$表示從$1$到$u$點的所有路徑里,比最短路多$k$的路徑數。

則有

$dp[u][k]=\sum dp[v][dis[u]+k-w-dis[v]]$

$dis[x]$是從$1$到$x$的最短路。spfa預處理復雜度$O(n \times ??? )$。

$w$是邊權。$v$是所有有指向$u$的邊的點。

記憶化搜索即可。判0環的話,還沒有寫出完美的寫法,如果有不包含$1$的0環,那么一個未確定的$dp$值被訪問兩次說明存在。

復雜度$O(nk)$。

代碼

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
using namespace std;
const int N=100010;
const int inf=0x3f3f3f3f;
typedef long long ll;
inline int read(){
    int r=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=r*10+c-'0';c=getchar();}
    return r*f;
}
struct Edge{
    int to,nxt,w;
}e[N*2],E[N*2];
int head[N],Head[N],cnt=1;
void add(int u,int v,int w){
    e[cnt]=(Edge){v,head[u],w};
    head[u]=cnt;
    E[cnt]=(Edge){u,Head[v],w};
    Head[v]=cnt++;
}
int n,m,K,P;
bool inq[N];
int dis[N],f[N][51],c[N][51];
void spfa(){
    deque<int>q;
    memset(dis,63,sizeof dis);
    dis[1]=0;q.push_back(1);inq[1]=1;
    while(!q.empty()){
        int u=q.front();q.pop_front();inq[u]=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to,w=e[i].w;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!inq[v]){
                    if(!q.empty()&&dis[v]<=dis[q.front()])
                    q.push_front(v);
                    else q.push_back(v);
                }
            }
        }
    }
}bool ff=0;
void Clear(){
    n=m=K=P=0;
    memset(inq,0,sizeof inq);
    memset(f,-1,sizeof f);
    memset(head,0,sizeof head);
    memset(Head,0,sizeof Head);
    memset(c,0,sizeof c);
    memset(e,0,sizeof e);
    memset(E,0,sizeof E);cnt=1;
    ff=0;
}
void Init(){
    n=read(),m=read(),K=read(),P=read();
    while(m--){
        int u=read(),v=read(),w=read();
        add(u,v,w);
    }
}
 
int dfs(int u,int k){
    if(~f[u][k])return f[u][k];
    c[u][k]=1;
    f[u][k]=0;
    for(int i=Head[u];i;i=E[i].nxt){
        int v=E[i].to,w=E[i].w;
        int t=dis[u]+k-w-dis[v];
        if(t<0)continue;
        if(c[v][t])ff=1;
        f[u][k]+=dfs(v,t),f[u][k]%=P;
    }
    c[u][k]=0;
    return f[u][k];
}
void Solve(){
    spfa();
    f[1][0]=1;
    int ans=0;
    for(int j=0;j<=K;j++)
    ans+=dfs(n,j),ans%=P;
    dfs(n,K+1);
    if(ff){
        cout<<-1<<endl;
        return ;
    }
    printf("%d\n",ans);
}
int main(){
    int T=read();
    while(T--){
        Clear();
        Init();
        Solve();
    }
    return 0;
}
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM