[省选联考 2021]支配

支配

题解

支配树板子题。
不过貌似SPFA+bitset有75pts?

首先我们应该很容易想到对原图建一棵支配树出来。
考虑加一条边对于支配树的影响情况。
显然,加一条边只会让一个点的被支配集缩小,即有一个以前能支配点 u u u的点 z z z在加边后不能够继续支配点 u u u
记添加边 ( x , y ) (x,y) (x,y) x x x y y ylca d d d。很明显,点 z z z一定在 d d d y y y之间。
因为我们添加的到 y y y的方法是从 1 − > d − > x − > y 1->d->x->y 1>d>x>y,总不可能让原来点1到y路径 1 − > d − > y 1->d->y 1>d>y外其它点变了吧。显然 1 − > d 1->d 1>d的路径是不可能变的,变的只有可能是 d − > y d->y d>y中的点。
而且同时容易证明在 1 1 1 d d d的路径上所有点的儿子节点也是不可能变的,因为 y y y u u u存在横叉边当且仅当 u u u是它们两者lca的儿子,除此之外只存在返祖边。
所以我们可以将 1 − > d 1->d 1>d的路径及与这条路径上的点直接相连的点打上标记,再在原图上从 y y y开始跑一道dfs即可,答案就是能访问到的点的数量。

时间复杂度 O ( q n ) O\left(qn\right) O(qn)

源码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<time.h>
using namespace std;
#define MAXM 3005
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
typedef unsigned int uint;
typedef pair<int,int> pii;
const int INF=0x7f7f7f7f;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
struct edge{int to,nxt;};
struct Graph{
	int head[MAXM],tot;edge e[MAXM<<1];
	void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
}orG,reG,orD,unD,T;
int n,m,Q,dfn[MAXM],pre[MAXM],idx,father[MAXM],deg[MAXM],siz[MAXM],ans;
int anc[MAXM],minanc[MAXM],semi[MAXM],dep[MAXM],f[MAXM][15];
queue<int> q;bool vis[MAXM];
void dfs(int u,int fa){
	pre[dfn[u]=++idx]=u;
	for(int i=orG.head[u];i;i=orG.e[i].nxt){
		int v=orG.e[i].to;if(dfn[v])continue;
		dfs(v,u);father[v]=u;orD.addEdge(u,v);
	}
}
int findSet(int x){
	if(anc[x]==x)return x;int las=anc[x];anc[x]=findSet(anc[x]);
	if(dfn[semi[minanc[x]]]>dfn[semi[minanc[las]]])minanc[x]=minanc[las];
	return anc[x];
}
void SpTarjan(){
	for(int i=1;i<=n;i++)anc[i]=minanc[i]=semi[i]=i;
	for(int i=n;i>1;i--){
		int x=pre[i],minn=i;if(!x)continue;
		for(int j=reG.head[x];j;j=reG.e[j].nxt){
			int v=reG.e[j].to;if(!dfn[v])continue;
			if(dfn[v]<dfn[x])minn=min(dfn[v],minn);
			else findSet(v),minn=min(dfn[semi[minanc[v]]],minn);
		}
		semi[x]=pre[minn];anc[x]=father[x];orD.addEdge(semi[x],x);
	}
}
int lca(int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	for(int i=12;i>=0;i--)if(dep[f[a][i]]>=dep[b])a=f[a][i];
	if(a==b)return a;
	for(int i=12;i>=0;i--)if(f[a][i]^f[b][i])a=f[a][i],b=f[b][i];
	return f[a][0];
}
void build(int x){
	int res=unD.e[unD.head[x]].to;
	for(int i=unD.head[x];i;i=unD.e[i].nxt)res=lca(res,unD.e[i].to);
	dep[x]=dep[res]+1;f[x][0]=res;T.addEdge(res,x);
	for(int i=1;i<=12;i++)f[x][i]=f[f[x][i-1]][i-1];
}
void sakura(){
	while(!q.empty())q.pop();q.push(0);
	for(int i=1;i<=n;i++)
		for(int j=orD.head[i];j;j=orD.e[j].nxt)
			deg[orD.e[j].to]++,unD.addEdge(orD.e[j].to,i);
	for(int i=1;i<=n;i++)if(!deg[i])orD.addEdge(0,i),unD.addEdge(i,0),deg[i]++;
	while(!q.empty()){
		int t=q.front();q.pop();
		for(int i=orD.head[t];i;i=orD.e[i].nxt){
			int v=orD.e[i].to;deg[v]--;
			if(!deg[v])build(v),q.push(v);
		}
	}
}
void dosaka(int x,int fa){
	siz[x]=1;f[x][0]=fa;for(int i=1;i<=12;i++)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=T.head[x];i;i=T.e[i].nxt)dosaka(T.e[i].to,x),siz[x]+=siz[T.e[i].to];
}
void dosaka2(int x){
	if(!vis[x])vis[x]=1,ans++;
	for(int i=orG.head[x];i;i=orG.e[i].nxt)
		if(!vis[orG.e[i].to])dosaka2(orG.e[i].to);
}
signed main(){
	read(n);read(m);read(Q);
	for(int i=1;i<=m;i++){
		int u,v;read(u);read(v);
		orG.addEdge(u,v);reG.addEdge(v,u);
	}
	dfs(1,0);SpTarjan();sakura();memset(f,0,sizeof(f));dosaka(1,0);
	for(int i=1;i<=Q;i++){
		int s,t;read(s);read(t);int s_t=lca(s,t);
		if(s_t==t||dep[s_t]==dep[t]-1){puts("0");continue;}
		for(int j=1;j<=n;j++)vis[j]=0;ans=0;
		while(s_t){
			for(int j=T.head[s_t];j;j=T.e[j].nxt)vis[T.e[j].to]=1;
			vis[s_t]=1,s_t=f[s_t][0];
		}
		dosaka2(t);printf("%d\n",ans);
	}
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值