【題解】 P8287 「DAOI R1」Flame
阿新 • • 發佈:2022-11-29
題面傳送門
解決思路
本題解是對 這篇題解 部分內容的補充,討論的是兩種 \(\mathcal{O(m \log n)}\) 的做法。
大體思路都是一樣的,先預處理出每一條邊需要多少時間後才能連上,可以用 \(\text{BFS}\) 實現。
然後二分答案時間,在每個時間下連線當前已經通的邊。設點 \(i\) 第一次被“點燃”的時間為 \(dis_i\),當前二分的時間為 \(t\),則 \((u,v)\) 聯通的條件是 \(dis_u\le t \text{ and }dis_v\le t\),然後再新圖上判斷是否有環即可。
首先是 \(\text{Tarjan}\)。本題資料範圍較大,而且 \(\text{Tarjan}\)
具體實現就是常規的 \(\text{Tarjan}\),做完一個點 \(x\) 之後,若 \(low_x<dfn_x\) ,說明它可以通過非樹邊到達其祖先,也就是存在環了。另外,因為二分答案做多次 \(\text{Tarjan}\),所以每次要清空!
AC Code(Tarjan)
//If, one day, I finally manage to make my dreams a reality... //I wonder, will you still be there by my side? #include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false) #define TIE cin.tie(0),cout.tie(0) #define y1 cyy #define fi first #define se second #define cnt1(x) __builtin_popcount(x) #define mk make_pair #define pb push_back #define pii pair<int,int> #define psi pair<string,int> #define ls(x) (x<<1) #define rs(x) (x<<1|1) #define lbt(x) (x&(-x)) using namespace std; int n,m,k,u[2000005],v[2000005],b,dis[1000005]; int dfn[1000005],low[1000005],timer,head[4000005],tot,Head[1000005],Tot; int stk[1000005],top,col[1000005]; bool mark[1000005],vis[1000005],fl; struct node{ int to,nxt; }e[4000005]; struct NODE{ int to,nxt; }E[4000005]; queue<int> q; inline char gc(){ static char buf[1000000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; } inline int read(){ char c=gc();int res=0,f=1; for(;c<'0'||c>'9';c=gc()) if(c=='-')f=-1; for(;c>='0'&&c<='9';c=gc()) res=res*10+c-'0'; return res*f; } inline void write(int x){ static int sta[205],top=0; if(x<0)putchar('-'),x=-x; do{sta[top++]=x%10;x/=10;}while(x); while(top) putchar(sta[--top]+48); } inline void writesp(int x){ write(x);putchar(' '); } inline void writeln(int x){ write(x);putchar('\n'); } void add(int u,int v){ e[++tot]={v,head[u]},head[u]=tot; } void ADD(int u,int v){ E[++Tot]={v,Head[u]},Head[u]=Tot; } void init(){ for(int i=1;i<=m*2;i++) head[i]=0; for(int i=1;i<=n;i++) dfn[i]=low[i]=col[i]=0,vis[i]=0; tot=timer=0,fl=0; } void tarjan(int x,int fa){ if(fl) return ; dfn[x]=low[x]=++timer; for(int i=head[x];i;i=e[i].nxt){ int tmp=e[i].to; if(tmp==fa) continue; if(!dfn[tmp]){ tarjan(tmp,x); if(fl) return ; low[x]=min(low[x],low[tmp]); } else low[x]=min(low[x],dfn[tmp]); } if(low[x]<dfn[x]){ fl=1; return ; } } bool check(int mid){ init(); for(int i=1;i<=m;i++) if(dis[u[i]]<=mid&&dis[v[i]]<=mid){ add(u[i],v[i]),add(v[i],u[i]); } for(int i=1;i<=n;i++) if(!dfn[i]) top=0,tarjan(i,0); return fl; } signed main(){ n=read(),m=read(),k=read(); for(int i=1;i<=m;i++){ u[i]=read(),v[i]=read(); ADD(u[i],v[i]),ADD(v[i],u[i]); } for(int i=1;i<=n;i++) dis[i]=1e9; for(int i=1;i<=k;i++) b=read(),q.push(b),dis[b]=0; while(q.size()){ int x=q.front();q.pop(); vis[x]=1; for(int i=Head[x];i;i=E[i].nxt){ int tmp=E[i].to; if(vis[tmp]) continue; if(dis[tmp]>dis[x]+1){ dis[tmp]=dis[x]+1; q.push(tmp); } } } int l=0,r=n,ans=-1; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==-1) puts("Poor D!"); else writeln(ans); return 0; }
然後是並查集。並查集在本題中簡單很多,而且效率也更高。具體實現就是依次判斷每條邊,如可以連線的兩點已經在同一個連通塊中了,就說明有環,否則就連上。同樣注意要清空。
AC Code(DSU)
//If, one day, I finally manage to make my dreams a reality... //I wonder, will you still be there by my side? #include<bits/stdc++.h> #define IOS ios::sync_with_stdio(false) #define TIE cin.tie(0),cout.tie(0) #define y1 cyy #define fi first #define se second #define cnt1(x) __builtin_popcount(x) #define mk make_pair #define pb push_back #define pii pair<int,int> #define psi pair<string,int> #define ls(x) (x<<1) #define rs(x) (x<<1|1) #define lbt(x) (x&(-x)) using namespace std; int n,m,k,u[2000005],v[2000005],b,dis[1000005]; int Head[1000005],Tot; bool vis[1000005]; struct DSU{ int fa[1000005]; void init(int n){ for(int i=1;i<=n;i++) fa[i]=i; } int find(int x){ if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void merge(int x,int y){ fa[find(x)]=find(y); } bool query(int x,int y){ return find(x)==find(y); } }dsu; struct NODE{ int to,nxt; }E[4000005]; queue<int> q; inline char gc(){ static char buf[1000000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++; } inline int read(){ char c=gc();int res=0,f=1; for(;c<'0'||c>'9';c=gc()) if(c=='-')f=-1; for(;c>='0'&&c<='9';c=gc()) res=res*10+c-'0'; return res*f; } inline void write(int x){ static int sta[205],top=0; if(x<0)putchar('-'),x=-x; do{sta[top++]=x%10;x/=10;}while(x); while(top) putchar(sta[--top]+48); } inline void writesp(int x){ write(x);putchar(' '); } inline void writeln(int x){ write(x);putchar('\n'); } void ADD(int u,int v){ E[++Tot]={v,Head[u]},Head[u]=Tot; } bool check(int mid){ dsu.init(n); for(int i=1;i<=m;i++) if(dis[u[i]]<=mid&&dis[v[i]]<=mid){ if(dsu.query(u[i],v[i])) return 1; else dsu.merge(u[i],v[i]); } return 0; } signed main(){ n=read(),m=read(),k=read(); for(int i=1;i<=m;i++){ u[i]=read(),v[i]=read(); ADD(u[i],v[i]),ADD(v[i],u[i]); } for(int i=1;i<=n;i++) dis[i]=1e9; for(int i=1;i<=k;i++) b=read(),q.push(b),dis[b]=0; while(q.size()){ int x=q.front();q.pop(); vis[x]=1; for(int i=Head[x];i;i=E[i].nxt){ int tmp=E[i].to; if(vis[tmp]) continue; if(dis[tmp]>dis[x]+1){ dis[tmp]=dis[x]+1; q.push(tmp); } } } int l=0,r=n,ans=-1; while(l<=r){ int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } if(ans==-1) puts("Poor D!"); else writeln(ans); return 0; }