P7520 [省选联考 2021 A 卷] 支配(题解)
题意
给定一张
对于任意一个点
现在有
数据保证给出的图
对于所有测试数据:
O(n(n+m)) 求受支配集
从
因此找到所有这样的
这等价于考察加边
- 找到路径
1\to x\to y \to v ,其中1\to x,y\to v 都不经过fa[v] ; -
-
- 如果先枚举 $v$,再在反图上去掉 $fa[v]$ 后做dfs将复杂度巨大。 - 可以事先枚举 $v$,在反图上去掉 $fa[v]$ 后做dfs,能到的点都是 $y$。即把 $v$ 存到 $y$ 的邻接表 $adj[y]$ 中。
对每组询问
- 枚举
v\in adj[y] ,查看1\rightarrow x 是否可以不经过fa[v] ,如可以,在v 的子树上打标记。
#include<bits/stdc++.h>
using namespace std;
const int N=3000+5;
vector<int> Adj[N],szpj[N],T[N],AdjT[N],Adj1[N];
int n,m,f[N];
bool vis[N],vis0[N];
void dfs(int u,int no){
vis[u]=1;
for(auto v:Adj[u]){
if(vis[v]||v==no) continue;
dfs(v,no);
}
}
void dfs0(int u){
vis0[u]=1;
for(auto v:Adj[u]){
if(vis0[v]) continue;
dfs0(v);
}
}
int sz[N],pre[N],t=0;
void dfsT(int u,int fa){
sz[u]=1;
pre[u]=++t;
for(auto v:T[u]){
if(v==fa) continue;
dfsT(v,u);
sz[u]+=sz[v];
}
}
void dfs1(int u,int no,int U){
if(u==no) return;
vis[u]=1;
Adj1[u].push_back(U);
for(auto v:AdjT[u]){
if(vis[v]||v==no) continue;
dfs1(v,no,U);
}
}
int c[N];
int main(){
int q;
cin>>n>>m;
cin>>q;
int u,v;
for(int i=1;i<=m;i++){
cin>>u>>v;
Adj[u].push_back(v);
AdjT[v].push_back(u);
}
dfs0(1);
// speical for node 1
for(int i=1;i<=n;i++){
szpj[i].push_back(1);
}
for(int i=2;i<=n;i++){
memset(vis,0,sizeof vis);
dfs(1,i);
for(int j=1;j<=n;j++){
if(vis0[j]&&vis[j]==0){
szpj[j].push_back(i);
}
}
}
for(int i=2;i<=n;i++){
int maxj=0;
for(auto j:szpj[i]){
if(j==i) continue;
if(szpj[j].size()>szpj[maxj].size()) maxj=j;
}
T[maxj].push_back(i);
f[i]=maxj;
}
dfsT(1,0);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof vis);
dfs1(i,f[i],i);
}
for(int i=1;i<=q;i++){
cin>>u>>v;
memset(c,0,sizeof c);
int ans=0;
for(auto j:Adj1[v]){
if(f[j]==0) continue;
if(!(pre[u]>=pre[f[j]]&&pre[u]<pre[f[j]]+sz[f[j]])){
c[pre[j]]++;
c[pre[j]+sz[j]]--;
}
}
for(int j=1;j<=n;j++) c[j]+=c[j-1];
for(int j=1;j<=n;j++) ans+=!!c[j];
cout<<ans<<"\n";
}
return 0;
}