1. 程式人生 > >【逛公園】

【逛公園】

我在有0環的圖裡跑了最短路計數

我可能已經是個廢物啦

很早之前就想寫這道題啦,但是太菜了發現自己不會,今天終於寫啦

首先我們建圖的時候建出一個正圖還有一個反圖,我們對著這兩個圖分別跑兩次最短路,求出\(1\)到所有點的最短路,以及所有點到\(n\)的最短路

如果不考慮無解的情況,我們現在就可以大力記搜了

我們設\(dp[x][j]\)表示從\(x\)走到\(n\)相比\(x\)\(n\)的最短路多走了\(j\)的路徑條數

之後大力記搜即可,\(ans=\sum_{i=0}^Kdp[1][i]\)

我們去記憶化搜尋就行了,比如說從\(dp[i][now]\)轉移到\(j\)這個點,我們轉移的條件就是從\(i\)

\(j\)有一條邊相連,之後由於\(dp[i][now]\)這個狀態要走的路程是\(dis[i]+now\),之後走了一條\(dis(i,j)\)的路徑,到了\(j\)點還要走的就是\(dis[i]+now-dis(i,j)\),相比最短路多走的就是\(dis[i]+now-dis(i,j)-dis[j]\),於是我們直接在記搜裡這樣轉移就好了

之後邊界條件就是\(dp[n][0]=1\)

再來考慮一下如何判斷無解的情況

顯然如果有\(0\)環,而且環裡有一個點到\(1\)\(n\)的最短路的和走的冤枉路小於\(K\),那麼就可以在這個環裡一直繞下去,就會出現無解了

顯然我們只需要考慮\(0\)

環的話,我們只需要建一張只有\(0\)邊的圖就行了,對這張圖跑一個\(topsort\)或者\(tarjan\),之後判斷環裡是否有這種點就可以啦

其實不不是非常難,主要就是\(dp\)的思想還有最簡單的圖論

但是我非常sb,我對於存在\(0\)環的圖跑了最短路計數,顯然這種圖的最短路條數是無限的,這樣跑出來顯然是錯的

程式碼

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define maxn 100005
#define LL long long
#define mp std::make_pair
int n,m,K,num[2],cnt;
int mod;
typedef std::pair<int,int> pii;
std::priority_queue<pii,std::vector<pii>,std::greater<pii> > q;
struct node
{
    int v,nxt,w;
}e[2][maxn<<1];
struct ZE
{
    int v,nxt;
}E[maxn<<1];
int Head[maxn];
inline void write(int x) 
{
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
inline void add(int x,int y)
{
    E[++cnt].v=y;
    E[cnt].nxt=Head[x];
    Head[x]=cnt;
}
int dp[maxn][51],Q[maxn],r[maxn];
int head[2][maxn],dis[2][maxn];
int f[maxn];
inline int read()
{
    char c=getchar();
    int x=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9')
      x=(x<<3)+(x<<1)+c-48,c=getchar();
    return x;
}
inline void add_edge(int x,int y,int z,int opt)
{
    e[opt][++num[opt]].v=y;
    e[opt][num[opt]].nxt=head[opt][x];
    e[opt][num[opt]].w=z;
    head[opt][x]=num[opt];
}
inline void dij(int s,int o)
{
    memset(f,0,sizeof(f));
    while(!q.empty()) q.pop();
    dis[o][s]=0;
    if(o) dp[s][0]=1;
    q.push(mp(dis[o][s],s));
    while(!q.empty())
    {
        int k=q.top().second;
        q.pop();
        if(f[k]) continue;
        f[k]=1;
        for(re int i=head[o][k];i;i=e[o][i].nxt)
        if(dis[o][e[o][i].v]>dis[o][k]+e[o][i].w)
        {
            dis[o][e[o][i].v]=dis[o][k]+e[o][i].w;
            q.push(mp(dis[o][e[o][i].v],e[o][i].v));
        }
    }
}
int dfs(int x,int now)
{
    if(dp[x][now]) return dp[x][now]%mod;
    for(re int i=head[0][x];i;i=e[0][i].nxt)
    if(dis[1][x]-dis[1][e[0][i].v]-e[0][i].w+now>=0)
        dp[x][now]=(dp[x][now]+dfs(e[0][i].v,dis[1][x]-dis[1][e[0][i].v]-e[0][i].w+now))%mod;
    return dp[x][now]%mod;
}
int main()
{
    int T=read();
    while(T--)
    {
        cnt=num[0]=num[1]=0;
        memset(Head,0,sizeof(Head));
        memset(head,0,sizeof(head));
        memset(dis,0x7f,sizeof(dis));
        memset(dp,0,sizeof(dp));
        memset(Q,0,sizeof(Q));
        memset(r,0,sizeof(r));
        n=read(),m=read(),K=read(),mod=read();
        int x,y,z;
        for(re int i=1;i<=m;i++)
        {
            x=read(),y=read(),z=read();
            if(!z) add(x,y),r[y]++;
            add_edge(x,y,z,0),add_edge(y,x,z,1);
        }
        dij(1,0),dij(n,1);
        int tot=0;
        for(re int i=1;i<=n;i++)
            if(!r[i]) Q[++tot]=i;
        for(re int i=1;i<=tot;i++)
        {
            int t=Q[i];
            for(re int j=Head[t];j;j=E[j].nxt)
            {
                r[E[j].v]--;
                if(!r[E[j].v]) Q[++tot]=E[j].v;
            }
        }
        int flag=0;
        for(re int i=1;i<=n;i++)
        if(r[i]&&dis[0][i]+dis[1][i]<=dis[0][n]+K)
        {
            puts("-1");
            flag=1;
            break;
        }
        if(flag) continue;
        int ans=0;
        for(re int i=0;i<=K;i++) ans=(ans+dfs(1,i))%mod;
        write(ans),putchar(10);
    }
    return 0;
}