切了这题就 AK 省选 day2 了
先建出支配树(提示的很明显了吧)。最终肯定是要一个 (mathrm O(nq)) 算法对吧。(n,m) 同阶,复杂度都用 (n) 表示。
首先很显然的事情是,加边之后每个点的支配点集合只会有元素逃跑不会新加。容易发现,设 (a=mathrm{LCA}(x,y)),如果 (operatorname{dis}(a,y)>1),则 (operatorname{subtree}(y)) 的支配点集合全部改变,方案是 (1 o a o x o y) 绕过 (idom_y)。简单证明一下为什么一定能找到一条这样的绕过 (idom_y) 的路径(虽然没啥用两千三百三十三):考虑 (1 o x) 的支配点路径 (1=x_0 o x_1 ocdots o x_s=x),由于 (x_i o x_{i+1}) 的所有可能点集合为 (operatorname{subtree}(x_i)-operatorname{subtree}(x_{i+1})-{x_i}),容易发现每个点最多出现在其中一段中。那么 (idom_y) 出现的那段就是 (x_i=a),于是只需要 ban 一次 (idom_y),由于不支配,显然能做到。
但是除了 (operatorname{subtree}(y)) 并不知道其它点的支配点集合改不改变……例如 (y) 能一步到达它的的兄弟 (z),这时候 (operatorname{subtree}(z)) 的支配点集合也全部改变。所以我们需要一个更普遍的结论。
一个想法是,如果一个点支配点集合改变,是不是一定有它的最近支配点改变?(operatorname{subtree}(y)-{y}) 全是反例……进一步发现,(z) 支配点集合改变当且仅当 (idom_z) 改变或 (idom_z) 支配点集合改变,正确性显然。那其实就依然可以只判每个点的最近支配点是否改变,然后 dfs 一波。
那其实就是看对每个点 (z),去掉 (idom_z) 加上 ((x,y)) 之后 (1) 是否能到 (z)。由于复杂度允许平方,那这其实就非常简单。不加 ((x,y)) 的时候去掉 (idom_z) 显然不存在 (1 o z),但是某些其它点被 (1) 到达;还有一些到达 (z),但不包括 (1)。如果 (x) 属于前者,(y) 属于后者,那就 win 了,否则 lose。那就平方预处理就好了。
由于允许平方,支配树可以平方暴力建非常好写,方法是去掉每个点 dfs,支配所有不能到的点,每个点取在 dfs 树上 dfn 第二大的支配点作为最近支配点。以及理论上来说 dfs 占的复杂度是 (mathrm O(n(n+q))),但其实 (mathrm O(nq)) 都是在同一棵支配树上 dfs,对这类任务简单的 dfs 有一个小 trick:直接按 dfn 正序或倒序遍历,就不用享受递归大常数和 vector
大常数了。所以 dfs 的复杂度其实只有 (mathrm O!left(n^2
ight)),你别看这也 2e7 了,果断用链式前向星,不开 O2 AC。
code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=6010;
int n,m,qu;
struct addedge{
int sz,head[N],nxt[N],val[N];
addedge(){sz=0;memset(head,0,sizeof(head));}
void ae(int x,int y){
nxt[++sz]=head[x],head[x]=sz,val[sz]=y;
}
}nei,rnei;
int dfn[N],nowdfn,mng[N];
bool cd(int x,int y){return dfn[x]<dfn[y];}
void dfs(int x=1){
mng[dfn[x]=++nowdfn]=x;
// cout<<x<<" dfn is "<<dfn[x]<<"!
";
for(int i=nei.head[x];i;i=nei.nxt[i]){
int y=nei.val[i];
if(!dfn[y])dfs(y);
}
}
bool vis[N];
void dfs_ban(int x=1){
vis[x]=true;
for(int i=nei.head[x];i;i=nei.nxt[i]){
int y=nei.val[i];
if(!vis[y])dfs_ban(y);
}
}
void rdfs(int x=1){
vis[x]=true;
for(int i=rnei.head[x];i;i=rnei.nxt[i]){
int y=rnei.val[i];
if(!vis[y])rdfs(y);
}
}
int idom[N];
vector<int> son[N];
bool dp1[N][N],dp2[N][N];
bool chg[N];
void dfs_dom(int x=1){
mng[dfn[x]=++nowdfn]=x;
for(int i=0;i<son[x].size();i++)dfs_dom(son[x][i]);
}
int main(){
cin>>n>>m>>qu;
while(m--){
int x,y;
scanf("%d%d",&x,&y);
nei.ae(x,y),rnei.ae(y,x);
}
dfs();
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
vis[i]=true;if(!vis[1])dfs_ban();
for(int j=1;j<=n;j++)if(!vis[j])/*cout<<j<<" is dominated by "<<i<<"!
",*/idom[j]=max(idom[j],i,cd);
}
// for(int i=1;i<=n;i++)cout<<idom[i]<<" ";puts("!");
for(int i=2;i<=n;i++){
memset(vis,0,sizeof(vis));
vis[idom[i]]=true;if(!vis[1])dfs_ban();
for(int j=1;j<=n;j++)dp1[i][j]=j!=idom[i]&&vis[j];
memset(vis,0,sizeof(vis));
vis[idom[i]]=true;if(!vis[i])rdfs(i);
for(int j=1;j<=n;j++)dp2[i][j]=j!=idom[i]&&vis[j];
}
for(int i=2;i<=n;i++)son[idom[i]].pb(i);
memset(dfn,0,sizeof(dfn)),memset(mng,0,sizeof(mng)),nowdfn=0;
dfs_dom();
while(qu--){
int x,y;
scanf("%d%d",&x,&y);
for(int i=2;i<=n;i++)chg[i]=dp1[i][x]&&dp2[i][y];
int ans=0;
for(int i=2;i<=n;i++)ans+=(chg[mng[i]]|=chg[idom[mng[i]]]);
cout<<ans<<"
";
}
return 0;
}