1. 程式人生 > >【luogu1685】遊覽 拓撲排序+DP

【luogu1685】遊覽 拓撲排序+DP

題目描述

順利通過了黃藥師的考驗,下面就可以盡情遊覽桃花島了!

你要從桃花島的西頭開始一直玩到東頭,然後在東頭的碼頭離開。可是當你遊玩了一次後,發現桃花島的景色實在是非常的美麗!!!於是你還想乘船從桃花島東頭的碼頭回到西頭,再玩一遍,但是桃花島有個規矩:你可以遊覽無數遍,但是每次遊玩的路線不能完全一樣。

我們把桃花島抽象成了一個圖,共\(n\)個點代表路的相交處,\(m\)條邊表示路,邊是有向的(只能按照邊的方向行走),且可能有連線相同兩點的邊。輸入保證這個圖沒有環,而且從西頭到東頭至少存在一條路線。兩條路線被認為是不同的當且僅當它們所經過的路不完全相同。

你的任務是:把所有不同的路線遊覽完一共要花多少時間?

輸入輸出格式

輸入格式:

\(1\)行為\(5\)個整數:\(n、m、s、t、t0\),分別表示點數,邊數,島西頭的編號,島東頭的編號(編號是從1到n)和你乘船從島東頭到西頭一次的時間。

以下\(m\)行,每行3個整數:\(x、y、t\),表示從點x到點y有一條行走耗時為t的路。

每一行的多個數據之間用一個空格隔開,且:\(2<=n<=10000; 1<=m<=50000;t<=10000;t0<=10000\)

輸出格式:

假設總耗時為\(total\),則輸出\(total\) \(mod\) \(10000\)的值(\(total\)\(10000\)取餘)。

輸入輸出樣例

輸入樣例#1:

3 4 1 3 7
1 2 5
2 3 7
2 3 10
1 3 15

輸出樣例#1:

56

說明

樣例解釋

共有\(3\)條路徑可以從點\(1\)到點\(3\),分別是\(1-2-3,1-2-3,1-3\)

時間計算為:\((5+7)+7 +(5+10)+7 +(15)=56\)

題解

我們定義

\(cnt[i]\)表示到點\(i\)的次數;

\(dis[i]\)表示到點\(i\)的總路徑長度。

所以\(ans=dis[t]+(cnt[t]-1)*t0\)

如何去轉移\(cnt\)\(dis\)陣列呢

\(dfs\)一遍不就行了

考慮一條邊從\(u\)\(v\)

,邊權為\(w\)

\(dis[v]=dis[v]+dis[u]+cnt[u]*w;\)

\(cnt[v]+=cnt[u];\)

初始化:\(cnt[s]=1\)

但是絕對不能直接\(dfs\)去遍歷,只能得\(20\)分。

我一開始就直\(dfs\),還是太菜了(20分)。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#define ll long long
#define R register
#define mod 10000
#define N 50005
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
int n,m,s,t,ti,tot,h[N];
ll cnt[N],dis[N];
struct node{
    int nex,to,dis;
}edge[N<<1];
inline void add(R int u,R int v,R int w){
    edge[++tot].nex=h[u];
    edge[tot].to=v;
    edge[tot].dis=w;
    h[u]=tot;
}
inline void dfs(R int x){
    for(R int i=h[x];i;i=edge[i].nex){
        R int xx=edge[i].to;
        (dis[xx]+=dis[x]+cnt[x]*edge[i].dis)%=mod;
        (cnt[xx]+=cnt[x])%=mod;
        dfs(xx);
    }
}
int main(){
    read(n);read(m);read(s);read(t);read(ti);
    for(R int i=1,u,v,w;i<=m;i++){
        read(u);read(v);read(w);
        if(u!=v)add(u,v,w);
    }
    cnt[s]=1;
    dfs(s);
    printf("%lld\n",(dis[t]+(cnt[t]-1)*ti)%mod);
    return 0;
}

為什麼這樣不對??

因為有一些點的資訊我們還沒有收集全面就用它去更新其他點了。

如何解決(感謝\(wtx\)大佬指導),

拓撲排序呀,當一個點入度為0時就說明已經沒有點可以去更新它了,說明它的資訊收集已經完全了。

正確的程式碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cctype>
#define ll long long
#define R register
#define mod 10000
#define N 50005
using namespace std;
template<typename T>inline void read(T &a){
    char c=getchar();T x=0,f=1;
    while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
    a=f*x;
}
int n,m,s,t,ti,tot,h[N],in[N];
ll cnt[N],dis[N];
struct node{
    int nex,to,dis;
}edge[N<<1];
inline void add(R int u,R int v,R int w){
    edge[++tot].nex=h[u];
    edge[tot].to=v;
    edge[tot].dis=w;
    h[u]=tot;
    in[v]++;
}
inline void dfs(R int x){
    for(R int i=h[x];i;i=edge[i].nex){
        R int xx=edge[i].to;
        (dis[xx]+=dis[x]+cnt[x]*edge[i].dis)%=mod;
        (cnt[xx]+=cnt[x])%=mod;
        --in[xx];//拓撲排序
        if(!in[xx])dfs(xx);
    }
}
int main(){
    read(n);read(m);read(s);read(t);read(ti);
    for(R int i=1,u,v,w;i<=m;i++){
        read(u);read(v);read(w);
        if(u!=v)add(u,v,w);
    }
    cnt[s]=1;
    dfs(s);
    printf("%lld\n",(dis[t]+(cnt[t]-1)*ti)%mod);
    return 0;
}