把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷7520】[省选联考 2021 A 卷] 支配(暴力支配树)

点此看题面

  • 给定一张\(n\)个点\(m\)条边的有向图,以\(1\)号点为出发点。
  • 每次询问加入一条边求有多少个点的受支配集发生了变化。
  • \(n\le3\times10^3,q\le2\times10^4\)

暴力支配树

暴力建支配树其实很简单的,就是直接从支配树的定义出发,不要像我一样想太多。。。

首先我们求出每个点的支配集(删除这个点从\(1\)开始\(dfs\)不能到达的点),反过来也就求出了每个点的受支配集。

然后我们建树,每次取出一个被支配集中只剩自己的节点\(k\),接着把它从所有它支配的点\(i\)的被支配集中删去。如果\(i\)的支配集被删得只剩下自己了,就在支配树上把\(k\)设定为\(i\)的父节点。

总的来说就是一个类似于拓扑排序的过程。

受支配集的变化讨论

一个点\(i\)的受支配集变化了,由于是加边,只会变小而不会变大。

因此,我们可以根据支配树上\(i\)父节点\(fa_i\)是否被移出\(i\)的受支配集分成两类。

如果\(fa_i\)没有被移出,则从\(i\)的受支配集中被移出的点\(z\)必然也会从\(fa_i\)的受支配集中移出(否则就意味着从\(1\)\(fa_i\)必然经过\(z\),而从\(1\)\(fa_i\)再到\(i\)可以不经过\(z\),显然矛盾)。同时我们又发现,从\(fa_i\)的受支配集中被移出的点\(z\)必然也会从\(i\)的受支配集中移出。

综上所述我们得出结论:一个父节点没有被移出的节点的受支配集是否变化只取决于父节点的受支配集是否变化。也就是说,一个点的受支配集改变,将会影响到支配树上整个子树的受支配集。

所以我们实际上只需要判断每一个\(fa_i\)是否会被移出\(i\)的支配集,然后在\(dfs\)序列上给\(i\)的子树打标记即可。

父节点是否会被移出

加入一条边\(x\rightarrow y\)\(fa_i\)会被移出支配集,意味着存在一条从\(1\)\(x\),再从\(y\)\(i\)的路径不经过\(fa_i\)

存在一条\(1\rightarrow x\)的路径不经过\(fa_i\),意味着\(fa_i\)不支配\(x\)

存在一条\(y\rightarrow i\)的路径不经过\(fa_i\),只要事先预处理出不经过\(fa_i\)能够到\(i\)的点集(删除\(fa_i\)\(i\)开始沿反向边\(dfs\)),判断\(y\)是否在点集中即可。

每次询问暴力枚举所有\(i\)判断,在\(dfs\)序列中打上子树差分标记,最后再扫一遍求出有多少标记不为\(0\)的位置即可。

代码:\(O(nq)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000
#define pb emplace_back
#define FOR(G) for(vector<int>::iterator it=G.begin();it!=G.end();++it)
using namespace std;
int n,m,s[N+5];vector<int> G[N+5],iG[N+5];
namespace DminatorTree//暴力支配树
{
	int d,fa[N+5],dI[N+5],dO[N+5],w[N+5][N+5],g[N+5][N+5];vector<int> T[N+5];
	I void Init(CI x) {dI[x]=++d;FOR(T[x]) Init(*it);dO[x]=d;}//预处理dfs序
	I void dfs(CI x,CI o) {w[o][x]=0;FOR(G[x]) w[o][*it]&&*it^o&&(dfs(*it,o),0);}//删除o后从1出发dfs
	I void idfs(CI x,CI o) {g[o][x]=1;FOR(iG[x]) !g[o][*it]&&*it^fa[o]&&(idfs(*it,o),0);}//删除fa后从o出发沿反向边dfs
	int q[N+5],deg[N+5];I void Build()//建支配树
	{
		RI i,j;for(i=1;i<=n;++i) {for(j=1;j<=n;++j) w[i][j]=1;for(i^1&&(dfs(1,i),0),j=1;j<=n;++j) i^j&&w[i][j]&&++deg[j];}//求出支配集
		RI k,H=1,L=1;q[1]=1;W(H<=L) for(k=q[H++],i=1;i<=n;++i) k^i&&w[k][i]&&!--deg[i]&&(fa[q[++L]=i]=k,T[k].pb(i),0);//拓扑建支配树
		for(Init(1),i=2;i<=n;++i) idfs(i,i);//对每个点预处理不经过其父节点能到达它的点集
	}
}using namespace DminatorTree;
int main()
{
	RI Qt,i,x,y;for(scanf("%d%d%d",&n,&m,&Qt),i=1;i<=m;++i) scanf("%d%d",&x,&y),G[x].pb(y),iG[y].pb(x);
	RI t;Build();W(Qt--)
	{
		for(scanf("%d%d",&x,&y),i=2;i<=n;++i) !w[fa[i]][x]&&g[i][y]&&(++s[dI[i]],--s[dO[i]+1]);//枚举每个点判断存在1到x,y到i不经过fa[i]的路径
		for(t=0,i=2;i<=n;++i) t+=(s[i]+=s[i-1])>0;for(printf("%d\n",t),i=2;i<=n;++i) s[i]=0;//差分标记求和判断
	}return 0;
}
posted @ 2021-05-21 08:58  TheLostWeak  阅读(97)  评论(0编辑  收藏  举报