• P7520[省选联考 2021 A 卷]支配


    正题

    题目链接:https://www.luogu.com.cn/problem/P7520


    题目大意

    给出\(n\)个点\(m\)条边的一张有向图,一号点为起始点,\(q\)次独立的询问加入一条边后有多少个点的支配集发生了变化。

    \(1\leq n\leq 3000,1\leq m\leq 2\times n,1\leq q\leq 2\times 10^4\)


    解题思路

    首先我们肯定是先建一棵支配树,可以直接\(O(n^2)\)搞。

    具体的做法我是从\(1\sim n\)枚举,然后枚举到\(x\)时把\(x\)删掉从\(1\)开始遍历,如果对于一个点\(y\)满足\(y\)不能到达且\(fa_y\)能够到达,那么\(fa_y\)改为\(x\)即可。

    然后考虑一条边\(x\rightarrow y\)能改变支配集的话,防止麻烦我们对于每个点\(x\)只考虑它的父节点\(fa_x\),如果\(1\sim y\)存在一个点\(x\)使得\(fa_x\)不再支配\(x\),那么\(y\)的支配集肯定改变。

    那么考虑对于一个边\(x\rightarrow y\),如果存在一条路径\(1\rightarrow x\rightarrow y\rightarrow i\)且不经过\(i\)的父节点,那么\(i\)整个子树的支配集都会改变,首先条件是\(1\rightarrow x\)不经过\(fa_i\),这个很简单,如果\(x\)不在\(fa_i\)的子树内就好了。然后是\(y\rightarrow i\)这个条件,也很好搞,枚举点\(i\),把\(fa_i\)删去,然后看\(i\)走反图能到达的点,这些点都可以作为\(y\),提前\(O(n^2)\)预处理就好了。

    然后询问的时候我们暴力枚举所有点判断是否合法,然后树上差分来统计答案就好了。

    时间复杂度:\(O(n^2+nq)\)


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    using namespace std;
    const int N=3010;
    int n,m,q,cnt,ans,fa[N],v[N],rfn[N],ed[N];
    vector<int> F[N],G[N],T[N];
    bool f[N][N];
    void dfs(int x){
    	if(v[x])return;v[x]=1;
    	for(int i=0;i<G[x].size();i++)
    		dfs(G[x][i]);
    	return;
    }
    void dfs2(int x){
    	rfn[x]=++cnt;
    	for(int i=0;i<T[x].size();i++)
    		dfs2(T[x][i]);
    	ed[x]=cnt;return;
    }
    void dfs3(int x){
    	if(v[x])return;v[x]=1;
    	for(int i=0;i<F[x].size();i++)
    		dfs3(F[x][i]);
    	return;
    }
    void dfs4(int x,int w){
    	w|=v[x];ans+=w;
    	for(int i=0;i<T[x].size();i++)
    		dfs4(T[x][i],w);
    	return;
    }
    int main()
    {
    	scanf("%d%d%d",&n,&m,&q);
    	for(int i=1,x,y;i<=m;i++){
    		scanf("%d%d",&x,&y);
    		G[x].push_back(y);
    		F[y].push_back(x);
    	}
    	for(int i=1;i<=n;i++){
    		memset(v,0,sizeof(v));
    		v[i]=2;dfs(1);v[0]=1;
    		for(int j=1;j<=n;j++)
    			if(!v[j]&&v[fa[j]])fa[j]=i;
    	}
    	for(int i=2;i<=n;i++)T[fa[i]].push_back(i);
    	dfs2(1);
    	for(int i=2;i<=n;i++){
    		memset(v,0,sizeof(v));
    		v[fa[i]]=2;dfs3(i);
    		for(int j=1;j<=n;j++)
    			if(v[j]==1)f[j][i]=1;
    	}
    	while(q--){
    		int x,y;ans=0;
    		memset(v,0,sizeof(v));
    		scanf("%d%d",&x,&y);
    		for(int i=1;i<=n;i++){
    			if(!f[y][i])continue;
    			if(rfn[fa[i]]<=rfn[x]&&ed[fa[i]]>=rfn[x])continue;
    			v[i]=1;
    		}
    		dfs4(1,0);
    		printf("%d\n",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    四、面向对象分析和设计全流程概述
    三、三大核心特征-继承
    二、三大核心特征-多态
    [第三章]一、三大核心特征-封装
    四、抽象类
    三、接口
    二、对象
    [第二章]一、类
    六、面向对象的迷思
    五、面向对象的应用范围
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/16138537.html
Copyright © 2020-2023  润新知