洛谷 P7520 - [省選聯考 2021 A 卷] 支配(支配樹)
阿新 • • 發佈:2021-08-25
迫真·支配樹不 sb 的題
,如果 \(fa_x\)(當然有些人喜歡稱這個東西為 \(idom_x\),反正能看懂就行了吧)的支配集改變,那麼 \(x\) 的支配集也會改變。因為根據支配樹的性質,每個點的支配集一定是該點到 \(1\) 路徑上所有點組成的集合,因此如果 \(fa_x\) 的支配集改變,就必然存在某個 \(fa_x\) 的祖先 \(y\),滿足存在路徑 \(1\to fa_x\) 且不經過 \(y\),這樣就存在路徑 \(1\to fa_x\to x\) 不經過 \(x\),\(y\) 就從 \(x\) 的支配集中消失了。同理,如果 \(fa_x\) 的支配集沒變,但 \(x\) 的支配集改變,必然是因為 \(fa_x\) 無法支配 \(x\),因為如果存在某個 \(x\) 的祖先 \(y\ne fa_x\),滿足 \(y\) 不再支配 \(x\) 且 \(y\) 能支配 \(fa_x\),那就能推出這個 \(1\to x\) 且不經過 \(y\) 的路徑肯定不經過 \(fa_x\),從而 \(fa_x\) 不支配 \(x\)。
不支配 \(x\) 那麼必然存在路徑 \(1\to u\to v\to x\) 滿足這條路徑不經過 \(fa_x\),而這又 obviously 等價於 \(1\to u,v\to x\) 均不經過 \(fa_x\),前者可以通過建支配樹時預處理出的“刪掉點 \(x\) 後是否存在 \(1\to y\) 的路徑的陣列 \(ban_{x,y}\)”求出,而關於後者我們發現都是形如”刪掉 \(fa_x\) 後 \(x\) 能否在反圖上到達 \(y\)“,因此我們再建一個 \(ban\_fa_{x,y}\) 維護這個東西即可。時間複雜度 \(\mathcal O(n^2+nq)\)
真·支配樹不 sb 的題。
首先題面已經瘋狂暗示咱們建出支配樹對吧,那咱就老老實實建唄。由於這題資料範圍允許 \(n^2\) 演算法通過,因此可以考慮 \(\mathcal O(n^2)\) 地建立支配樹,具體來說我們列舉每個點 \(x\),將這個點暫時地從圖中刪除,如果對於圖中另一個點 \(y\) 滿足刪除 \(x\) 後 \(1\) 不能到達 \(y\),那麼 \(x\) 就在 \(y\) 的支配集中,這樣我們再對整個 DAG DFS 一遍求出每個點的 DFS 序,然後取 DFS 序最大的點作為每個點在支配樹上的父親即可。
接下來考慮怎樣計算答案。首先顯然的一件事情是,我們加入一條邊後最多隻會讓某些點的支配集大小變小,而不會使支配集大小變大,因此我們只需考慮有哪些點在加入這條邊後,存在某個點原來能支配它而現在不能即可。注意到一個性質,就是對於一個點 \(x\)
因此我們考慮每次詢問對整棵樹進行 DFS,如果走到一個點發現 \(fa_x\) 不支配 \(x\),答案就加上 \(x\) 子樹的大小並 return
,那麼怎麼判斷 \(fa_x\) 是否支配 \(x\) 呢?顯然如果 \(fa_x\)
卡常技巧:交換 \(ban\) 和 \(ban\_fa\) 的兩維後,效率大約能快 25%,具體原理見這兒。
const int MAXN=3e3;
const int MAXM=MAXN<<1;
int n,m,qu;
struct graph{
int hd[MAXN+5],nxt[MAXM+5],to[MAXM+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
} g,rv_g,dt;
int dfn[MAXN+5],rid[MAXN+5],tim=0,fa[MAXN+5];
bool ban[MAXN+5][MAXN+5],ban_fa[MAXN+5][MAXN+5];
void dfs(int x){
rid[dfn[x]=++tim]=x;
for(int e=g.hd[x];e;e=g.nxt[e]){
int y=g.to[e];if(!dfn[y]) dfs(y);
}
}
void dfs_ban(int x,int ban_id){
if(x==ban_id) return;ban[x][ban_id]=1;
// printf("{%d,%d}\n",ban_id,x);
for(int e=g.hd[x];e;e=g.nxt[e]){
int y=g.to[e];
if(!ban[y][ban_id]) dfs_ban(y,ban_id);
}
}
void dfs_ban_fa(int x,int ban_id){
if(x==fa[ban_id]) return;ban_fa[x][ban_id]=1;
for(int e=rv_g.hd[x];e;e=rv_g.nxt[e]){
int y=rv_g.to[e];
if(!ban_fa[y][ban_id]) dfs_ban_fa(y,ban_id);
}
}
int siz[MAXN+5];
void dfssiz(int x){
siz[x]=1;
for(int e=dt.hd[x];e;e=dt.nxt[e]){
int y=dt.to[e];dfssiz(y);
siz[x]+=siz[y];
}
}
int X,Y,res=0;
void dfscalc(int x){
if(x^1){
if(ban[X][fa[x]]&&ban_fa[Y][x]){
res+=siz[x];return;
}
} for(int e=dt.hd[x];e;e=dt.nxt[e]){
int y=dt.to[e];dfscalc(y);
}
}
int main(){
scanf("%d%d%d",&n,&m,&qu);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
g.adde(u,v);rv_g.adde(v,u);
} dfs(1);for(int i=1;i<=n;i++) dfs_ban(1,i);
// for(int i=1;i<=n;i++) printf("%d %d\n",dfn[i],rid[i]);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
if(!ban[j][i]&&(i^j)) chkmax(fa[j],dfn[i]);
for(int i=2;i<=n;i++) fa[i]=rid[fa[i]];fa[1]=0;
for(int i=2;i<=n;i++) dt.adde(fa[i],i),dfs_ban_fa(i,i);
// for(int i=2;i<=n;i++) printf("%d\n",fa[i]);
dfssiz(1);
while(qu--){scanf("%d%d",&X,&Y);res=0;dfscalc(1);printf("%d\n",res);}
return 0;
}