[APIO2020] 交換城市
阿新 • • 發佈:2021-07-17
一、題目
二、解法
顯然是連通性問題,直接最小生成樹不好做,可以考慮 \(\tt kruskall\) 重構樹。
首先考慮 \(x,y\) 能相互到達的充要條件,其實除了連通塊是鏈的情況,都可以到達。轉化一下就是連通塊中存在度數大於等於 \(3\) 的點或者邊數大於等於點數。
我們把邊權從小到大排序然後依次加入,如果加入某條邊之後的連通塊不合法,那麼我們先維護這個連通塊的結構,再後面的邊把這個連通塊啟用的時候再替換根的邊權,詢問的時候直接查 \(\tt lca\) 即可。
三、總結
總結一下重構樹的適用範圍:對連通塊有特殊限制的連通性問題;再某個時刻的連通塊內查詢的問題。
#include <cstdio> #include <vector> #include <algorithm> #include "swap.h" using namespace std; const int M = 200005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,fa[M],p[M][20],dep[M],fl[M],val[M],d[M]; vector<int> g[M]; struct node { int u,v,c; bool operator < (const node &b) const { return c<b.c; } }a[M]; int find(int x) { if(x!=fa[x]) fa[x]=find(fa[x]); return fa[x]; } void uni(int x,int y,int c) { ++d[x];++d[y]; int u=find(x),v=find(y); if(u!=v) { fa[u]=fa[v]=++n; g[n].push_back(u); g[n].push_back(v); val[n]=c; fl[n]=fl[u]|fl[v]|(d[x]>=3)|(d[y]>=3); } else if(!fl[u]) fl[u]=1,val[u]=c; } void dfs(int u) { dep[u]=dep[p[u][0]]+1; if(!fl[u] && fl[p[u][0]]) fl[u]=1,val[u]=val[p[u][0]]; for(int i=1;i<20;i++) p[u][i]=p[p[u][i-1]][i-1]; for(int v:g[u]) p[v][0]=u,dfs(v); } int lca(int u,int v) { if(dep[u]<=dep[v]) swap(u,v); for(int i=19;i>=0;i--) if(dep[p[u][i]]>=dep[v]) u=p[u][i]; if(u==v) return u; for(int i=19;i>=0;i--) if(p[u][i]^p[v][i]) u=p[u][i],v=p[v][i]; return p[u][0]; } void init(int N,int M,vector<int> U,vector<int> V,vector<int> C) { n=N;m=M; for(int i=1;i<=2*n;i++) fa[i]=i; for(int i=0;i<m;i++) a[i+1]=node{U[i]+1,V[i]+1,C[i]}; sort(a+1,a+1+m); for(int i=1;i<=m;i++) uni(a[i].u,a[i].v,a[i].c); dfs(n); } int getMinimumFuelCapacity(int x,int y) { int t=lca(x+1,y+1); if(fl[t]) return val[t]; return -1; } /* signed main() { int n=read(),m=read(),q; vector<int> u,v,c; for(int i=1;i<=m;i++) { u.push_back(read()); v.push_back(read()); c.push_back(read()); } init(n,m,u,v,c);q=read(); while(q--) { int u=read(),v=read(); printf("%d\n",getMinimumFuelCapacity(u,v)); } } */