1 条题解

  • 0
    @ 2025-8-24 22:30:39

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Aly_
    B.Q.A.C.

    搬运于2025-08-24 22:30:39,当前版本为作者最后更新于2021-04-17 10:50:13,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    一个究极暴力做法

    题意简述

    • 给定一张有向图 GG,定义一个点 ii 的支配集是所有满足如下性质的点:删除该点后 11 不再可达 ii
    • 回答 qq 个询问 <pj,qj><p_j,q_j>,询问的是加入边 pjqjp_j\rightarrow q_j 后多少个点的支配集发生变化。
    • 1n30001\leq n\leq 30001q200001\leq q\leq 20000

    解法

    ​ 一步一步推。

    ​ 以下是考场上的思考:

    1. 加入一条边后一个点的支配集要么变小,要么不变。因为这条边使整张图 “ 更加联通 ”。
    2. 支配的传递关系:若 xx 支配 yyyy 支配 zz,则 xx 支配 zz。因为删除 xxyy 被割开,而 yy 一旦不可达 zz 也就随之不可达了。
    3. 支配的链式关系:若 xx 支配 aayy 也支配 aa,则 x,yx,y 之间也存在支配关系。如不然则必定有一种情况,使得删除其中一个点后,11 可从另一个点到达 aa
    4. 支配树:对于一个点 xx,设 DxD_x 为它的支配集,**则 DxD_x 中必定有且仅有一个点 faxfa_x,使得 DxD_x 中除 faxfa_x 的点都支配 yy。**这个性质可由上面的观察得出。我们新建一棵以 11 为根的树,其中包含所有 (x,fax)(x,fa_x) 无向边,此即为该无向图的支配树。
    5. xx 的支配集为支配树上所有 xx 的祖先。
    6. xx 的支配集中,faxfa_x 是支配集大小最大的那个点。这点可由前面得出。通过这点可以暴力地构建支配树。
    7. 加入一条边后,xx 的支配集发生改变时,要么 faxfa_x 的支配集发生改变要么 faxfa_x 不再是 xx 的支配点。这点可由上面支配树的定义看出。
    8. 加入一条边 pqp\rightarrow q 后,faxfa_x 不再是 xx 的支配点,当且仅当删除 faxfa_x11 可达 ppqq 可达 xx。可能有一些诸如 q=faxq=fa_x 的边角情况,特殊判掉即可。

    ​ 于是出现了一个很暴力的做法。该做法分为 O(n2)O(n^2) 的预处理和 O(nq)O(nq) 的询问两部分。

    ​ 依次遍历所有点,看看把它删掉后哪些点不再可达。这样可以求出每个点的支配集。由前面的第六点思考,我们得以构建支配树。构建完后,对于每个点 xx,我们把它的父亲删掉,看看哪些点仍从 11 可达,哪些点仍可达 xx。暴力 BFS,复杂度 O(n2)O(n^2)

    ​ 查询时在树上 DFS。运用第七点和第八点综合判定即可。单次复杂度 O(n)O(n)

    ​ 总复杂度 O(n2+nq)O(n^2+nq)。空间 O(n2)O(n^2)。可以通过本题。

    ​ 下面是巨丑考场代码。有微调。

    #include<bits/stdc++.h>
    #define N 3005
    using namespace std;
    int n,m,Q,p,q,fa[N+1],fl[N+1][N+1],t[N+1],tfl[N+1][N+1],gfl[N+1];
    vector<int>bi[N+1],fbi[N+1],o[N+1];
    pair<int,int>q0[100001];
    bool comp(int x,int y){return o[x].size()<o[y].size();}
    void putin(){
    	cin>>n>>m>>Q;
    	for(int i=1;i<=m;i++)cin>>p>>q,bi[p].push_back(q),fbi[q].push_back(p);
    	for(int i=1;i<=Q;i++)cin>>q0[i].first>>q0[i].second;
    }
    inline void dfs0(int x,int np){
    	fl[x][np]=1;
    	if(x==np)return;
    	for(int i=0;i<bi[x].size();i++){
    		int ni=bi[x][i];
    		if(!fl[ni][np])dfs0(ni,np);
    	}
    }
    void ycl(){
    	dfs0(1,0);
    	for(int i=1;i<=n;i++)t[i]=i;
    	for(int i=1;i<=n;i++){
    		dfs0(1,i);
    		for(int j=1;j<=n;j++)if(fl[j][0]&&!fl[j][i])o[j].push_back(i);
    	}
    	sort(t+1,t+n+1,comp);
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<o[i].size();j++){
    			if(o[o[i][j]].size()==o[i].size()-1){
    				fa[i]=o[i][j];break;
    			}
    		}
    	}
    }
    inline void dfs1(int x,int np,int nr){
    	if(x==nr)return;
    	tfl[x][np]=1;
    	for(int i=0;i<fbi[x].size();i++){
    		int ni=fbi[x][i];
    		if(!tfl[ni][np])dfs1(ni,np,nr);
    	}
    }
    void cal0(){
    	for(int i=2;i<=n;i++){
    		dfs1(i,i,fa[i]);
    	}
    	for(int i=1;i<=Q;i++){
    		int nans=0;
    		for(int j=1;j<=n;j++)if(fl[q0[i].first][fa[j]]&&tfl[q0[i].second][j]&&fa[j]!=1&&fa[j]!=q0[i].first)gfl[j]=1;
    		for(int j=1;j<=n;j++)if(gfl[fa[t[j]]])gfl[t[j]]=1;
    		for(int j=1;j<=n;j++)if(gfl[j])nans++,gfl[j]=0;
    		cout<<nans<<'\n';
    	}
    }
    signed main(){
    	putin();
    	ycl();
    	cal0();
    	return 0;
    }
    

    省选救命题

    • 1

    信息

    ID
    6682
    时间
    1000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者