Siruseri
城中的道路都是 的。不同的道路由路口連接。按照法律的規定, 在每個路口都設立了一個 Siruseri
銀行的 ATM
取款機。令人奇怪的是,Siruseri
的酒吧也都設在路口,雖然並不是每個路口都設有酒吧。
Banditji
計劃實施 Siruseri
有史以來最驚天動地的 ATM
搶劫。他將從市中心 出發,沿着單向道路行駛,搶劫所有他途徑的 ATM
機,最終他將在一個酒吧慶祝他的勝利。
使用高超的黑客技術,他獲知了每個 ATM
機中可以掠取的現金數額。他希望你幫助他計算從市中心出發最後到達某個酒吧時最多能搶劫的現金總數。他可以經過同一路口或道路任意多次。但只要他搶劫過某個 ATM
機後,該 ATM
機 裏面就不會再有錢了。
例如,假設該城中有 個路口,道路的連接情況如下圖所示:
市中心在路口 ,由一個入口符號 來標識,那些有酒吧的路口用雙圈來表示。每個 ATM
機中可取的錢數標在了路口的上方。在這個例子中,Banditji
能搶劫的現金總數爲 ,實施的搶劫路線是:。
有向圖的題目,很容易讓人想到有向無環圖(DAG
)。因爲 DAG
比有向有環圖時的情況要簡單得多。
所以,我們可以先用 tarjan
把原圖縮點,順便維護出每個 scc
(強連通分量)中所有 ATM
內錢的總量 和是否有酒店。
縮點後,原圖變爲了一個 DAG
。我們可以用拓撲算法求解。代碼如下:
爲什麼這麼做是可以的呢?因爲 Banditji
可以重複走一條路很多次。我們進入一個 scc
後,因爲 scc
內所有點可以直接或間接互相到達,所以我們一定要把它內的所有 ATM
全部搶完,否則不優。同時,因爲 Banditji
可以重複走一條路很多很多次,所以搶完後,無論他在什麼地方,他總能走到其它點從而到達其它 scc
。
如圖,藍圈表示一個 scc
,黑圈表示一個點。當 Banditji
在 SCC 1
中隨意走到容易一個節點時,他都可以沿邊走到 號點,從而到達 SCC 2
。所以,我們的算法是正確的。
下一個問題:如何求答案?很簡單,求出所有有酒店的 SCC
中最大的 值即可。正因爲它是一個 scc
,所以 Banditji
無論在天涯海角,他都可以走到那個酒店中結束他的行程。正符合題意!!!
做到這裏,這道題就算可以 AC
了。剩下的問題就是打 tarjan
算法的板子了。最後整理一下我們的思路:
-
tarjan
算法縮點——把有向有環圖變成DAG
。
-
- 建立一個只有
scc
的新圖。
- 建立一個只有
-
- 調用拓撲排序算法輔以 求解(即
topo_algorithm_and_dp
函數)
- 調用拓撲排序算法輔以 求解(即
-
- 輸出(即
print_the_answer
函數)
- 輸出(即
const int N=5e5+100;
struct edge{
int next,to;
}e[N],E[N];int H[N],Tot,h[N],tot;
inline void add(int a,int b){
e[++tot]=(edge){h[a],b};h[a]=tot;
}
inline void ADD(int a,int b){
E[++Tot]=(edge){H[a],b};H[a]=Tot;
}
bool have_a_bar[N];
int n,m,s,p,value[N];
void read_the_data(){
n=read();m=read();//專司輸入
for(int i=1,u,v;i<=m;i++){
u=read();v=read();add(u,v);
}
for(int i=1;i<=n;i++)
value[i]=read();
s=read();p=read();
for(int i=1;i<=p;i++)
have_a_bar[read()]=1;
}
int Stack[N],stack_top;
int dfn[N],low[N],dfscnt;
int belong[N],scc,num[N];
int money[N];bool bar[N];
void tarjan(int u){
Stack[++stack_top]=u;
low[u]=dfn[u]=++dfscnt;
for(int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (dfn[to]==0){
tarjan(to);//遞歸進行計算
low[u]=min(low[u],low[to]);
}
else if (belong[to]==0)
low[u]=min(low[u],dfn[to]);
}
if (dfn[u]==low[u]){
num[belong[u]=++scc]=1;
bar[scc]=have_a_bar[u];
money[belong[u]]=value[u];
while (Stack[stack_top]!=u){
int t=Stack[stack_top];
num[belong[t]=scc]++;
money[scc]+=value[t];
bar[scc]|=have_a_bar[t];
--stack_top;
}
--stack_top;
}
}
int ind[N],dp[N],ans;
void build_new_picture(){
for(int u=1;u<=n;u++)
for(int i=h[u];i;i=e[i].next){
register int to=e[i].to;
if (belong[u]!=belong[to]){
++ind[belong[to]];
ADD(belong[u],belong[to]);
}
}
}
void topo_algorithm_and_dp(){
queue<int> q;//拓撲所需的隊列
memset(dp,128,sizeof(dp));
for(int i=1;i<=scc;i++)
if (ind[i]==0) q.push(i);
dp[belong[s]]=money[belong[s]];
while (q.size()){
int u=q.front();q.pop();
for(int i=H[u];i;i=E[i].next){
register int to=E[i].to;
dp[to]=max(dp[to],dp[u]+money[to]);
if ((--ind[to])==0) q.push(to);
}
}
}
void print_the_answer(){
for(int i=1;i<=scc;i++)
if (bar[i]&&dp[i]>ans)
ans=dp[i];
printf("%d",ans);
}
int main(){
read_the_data();
for(int i=1;i<=n;i++)
if (!dfn[i]) tarjan(i);
build_new_picture();
topo_algorithm_and_dp();
print_the_answer();
return 0;
}
本代碼已經 AC(本人親自實驗)且無任何反作弊系統。請讀者放心。
代碼中的 read 函數就是快點函數(反正沒人看,乾脆不給了)