0926-Tarjan縮點-間諜網路(luogu P1262)
阿新 • • 發佈:2018-12-12
分析
很明顯,這道題與節點的度有關。如果一個點的入度為0,則我們必然要賄賂他。但是如果單純的考慮度就錯了。我們忽略了一種入度全部大於0的情況——環。樣例就是一個例子。這時如果我們再拓撲找環再去找最小值,我們就會花大量時間(畢竟邊很多)。這時就要用到Tarjan縮點。
Tarjan是一種很高效的求解有向圖的強連通分量的演算法,但是它的主要應用之一是縮點,也就是把整個強連通分量的一定資訊集中到一個點上,將其構成一個新圖。由於所有強連通分量的並集是所有點的並集,所以整個圖的相應性質不變。
我們把圖G轉化成一個由代表強連通分量的點構成的G’。我們只需記錄該分量中最小的節點權值。然後在G’中找到入度為0的點,列舉是否能被賄賂,如果不能則輸出NO,否則累加,到最後輸出。
這道題主要還是當做 tarjan 模板來做
程式碼
#include<bits/stdc++.h> #define N 3009 #define in read() using namespace std; int n,p,r; inline int read(){ char ch;int res=0; while((ch=getchar())<'0'||ch>'9'); while(ch>='0'&&ch<='9'){ res=(res<<3)+(res<<1)+ch-'0'; ch=getchar(); } return res; } struct node{bool co;int pay;} a[N]; int nxt[8009],head[N],to[8009],cnt; int dfn[N],low[N],dfs=0,sum=0,id[N],du[N],pp[N],mi[N]; bool insta[N]; stack<int > s; void add(int x,int y){ nxt[++cnt]=head[x];head[x]=cnt;to[cnt]=y;} void tarjan(int x){ s.push(x);insta[x]=1; dfn[x]=++dfs;low[x]=dfn[x]; for(int i=head[x];i;i=nxt[i]){ int y=to[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); } else if(insta[y]) low[x]=min(low[x],dfn[y]); } int i; if(dfn[x]==low[x]){ sum++;int minn=20009,minnh=3009; do{ i=s.top();s.pop(); insta[i]=0;id[i]=sum; minnh=min(minnh,i);//記錄這個環中最小的編號 if(a[i].co) minn=min(minn,a[i].pay);//記錄這個環中最小賄賂 }while(i!=x); pp[sum]=minn;mi[sum]=minnh; } } int main(){ int ans=0; n=in;p=in; memset(a,0,sizeof(a)); for(int i=1;i<=p;++i) { int k=in; a[k].co=1;a[k].pay=in; } r=in; int x,y; for(int i=1;i<=r;++i){ x=in;y=in; add(x,y); } for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i); for(int i=1;i<=n;++i){ for(int e=head[i];e;e=nxt[e]){ int j=to[e]; if(id[j]!=id[i]&&(pp[id[i]]<20009||du[id[i]]!=0)) du[id[j]]++; //只有當這個“點”之前的那個點是可以被到達/或被直接賄賂,這個點才可以增加度數 } } int num=0,flag=1,minn=3009; for(int i=1;i<=sum;++i){ if(!du[i]&&pp[i]==20009) { flag=0; minn=min(minn,mi[i]); } if(!du[i]&&pp[i]<20009) ans+=pp[i]; } if(flag) printf("YES\n%d",ans); else printf("NO\n%d",minn); return 0; }