专栏文章

P7520 [省选联考 2021 A 卷] 支配(题解)

P7520题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minj0oou
此快照首次捕获于
2025/12/02 03:12
3 个月前
此快照最后确认于
2025/12/02 03:12
3 个月前
查看原文

题意

给定一张 nn 个点 mm 条边的有向图 GG,对于任意两个点 u,vu, v,若从顶点 11 出发到达顶点 vv 的所有路径都需要经过顶点 uu,则称顶点 uu 支配顶点 vv。特别地,每个顶点支配其自身。
对于任意一个点 vv,我们将图中支配顶点 vv 的顶点集合称为 vv 的受支配集 DvD_v
现在有 qq 次互相独立的询问,每次询问给出一条有向边,请你回答在图 GG 中加入该条边后,有多少个顶点的受支配集发生了变化。
数据保证给出的图 GG 中,从 11 号点出发能到达所有其他点,并且图中不包含重边与自环。
对于所有测试数据:1n3×1031 \le n \le 3 \times {10}^31m2×n1 \le m \le 2 \times n1q2×1041 \le q \le 2 \times {10}^4

O(n(n+m))O(n(n+m)) 求受支配集

Dv=u=1n[P(1,u,v)]D_v=\sum_{u=1}^n [P(1,u,v)]
P(1,u,v)P(1,u,v) 表示从顶点 11 出发到达顶点 vv 的所有路径都需要经过顶点 uu,即去掉 uu 之后,从 11 出发将无法到达 vv
考虑朴素求 DvD_v,枚举一个 uu,dfs查看从 11 出发是否可以到 vv,如可以,则把 uu 加入 DvD_v
  • 这需要先枚举 vv,再枚举 uu,再dfs。
  • 显然可以反过来,先枚举 uu,再dfs,此时 uu 可以加入所有无法到达的点的受支配集。

求支配树

性质

  • 传递关系:若 xx 支配 yyyy 支配 zz,则 xx 支配 zz
  • 链式关系:若 xx 支配 aayy 也支配 aa,则 x,yx,y 之间也存在支配关系。如不然则必定有一种情况,使得删除其中一个点后,11 可从另一个点到达 aa

支配树

  • 每个点与直接支配点(受支配集中距离自己最近的点)连边
  • xx 的支配集为支配树上所有 xx 的祖先。

构建支配树

方法1

xx 的支配集中,faxfa_x 是支配集大小最大的那个点。

方法2

类似于拓扑排序,把支配关系看作有向边,首先把 11 入队,然后重复进行如下操作:
  • 取出队头 uu,对于所有受支配集中存在点 uu 的点,把 uu 从受支配集中删掉。
  • 对于所有没有入队并且受支配集中只剩下本身(支配集大小为 11 )的点 vv 入队,连边 (u,v)(u,v)

求解

对于加入的每条边 (xy)(x\to y),点 vv 的受支配集改变当且仅当:
  • fa[v]fa[v] 的受支配集改变;
  • 或者 fa[v]fa[v] 不再支配 vv
vv 向上一直追溯,一定存在一个位置是第二种情况(存在一个 fa[v]fa[v'] 不再支配 vv'),则这个 vv'在支配树中的子树中的点都受影响。(可以dfn序上子树差分)
因此找到所有这样的 vv' 即可。
这等价于考察加边 (x,y)(x,y) 之后,每个点 vvfafa 是否仍然支配它。对于点 vv
  • 找到路径 1xyv1\to x\to y \to v,其中 1x,yv1\to x,y\to v 都不经过 fa[v]fa[v]
  • 1x1\to x 不经过 fa[v]fa[v] 等价于: xx 不在 fa[v]fa[v] 的子树里。这可以利用dfn序直接判断);
  • yvy\to v 不经过 fa[v]fa[v] 等价于:在反图上有 vyv\to y 不经过 fa[v]fa[v]
    • 如果先枚举 vv,再在反图上去掉 fa[v]fa[v] 后做dfs将复杂度巨大。
    • 可以事先枚举 vv,在反图上去掉 fa[v]fa[v] 后做dfs,能到的点都是 yy。即把 vv 存到 yy 的邻接表 adj[y]adj[y] 中。
对每组询问 x,yx,y
  • 枚举 vadj[y]v\in adj[y],查看 1x1\rightarrow x 是否可以不经过 fa[v]fa[v],如可以,在 vv 的子树上打标记。
CPP
#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;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...