P7520 [省选联考 2021 A 卷] 支配 题解
一个究极暴力做法
题意简述
- 给定一张有向图
G ,定义一个点i 的支配集是所有满足如下性质的点:删除该点后1 不再可达i 。 - 回答
q 个询问<p_j,q_j> ,询问的是加入边p_j\rightarrow q_j 后多少个点的支配集发生变化。 -
解法
一步一步推。
以下是考场上的思考:
- 加入一条边后一个点的支配集要么变小,要么不变。因为这条边使整张图 “ 更加联通 ”。
- 支配的传递关系:若
x 支配y ,y 支配z ,则x 支配z 。因为删除x 后y 被割开,而y 一旦不可达z 也就随之不可达了。 - 支配的链式关系:若
x 支配a ,y 也支配a ,则x,y 之间也存在支配关系。如不然则必定有一种情况,使得删除其中一个点后,1 可从另一个点到达a 。 - 支配树:对于一个点
x ,设D_x 为它的支配集,则D_x 中必定有且仅有一个点fa_x ,使得D_x 中除fa_x 的点都支配y 。这个性质可由上面的观察得出。我们新建一棵以1 为根的树,其中包含所有(x,fa_x) 无向边,此即为该无向图的支配树。 -
x 的支配集中,fa_x 是支配集大小最大的那个点。这点可由前面得出。通过这点可以暴力地构建支配树。- 加入一条边后,
x 的支配集发生改变时,要么fa_x 的支配集发生改变,要么fa_x 不再是x 的支配点。这点可由上面支配树的定义看出。 - 加入一条边
p\rightarrow q 后,fa_x 不再是x 的支配点,当且仅当删除fa_x 后1 可达p ,q 可达x 。可能有一些诸如q=fa_x 的边角情况,特殊判掉即可。
于是出现了一个很暴力的做法。该做法分为
依次遍历所有点,看看把它删掉后哪些点不再可达。这样可以求出每个点的支配集。由前面的第六点思考,我们得以构建支配树。构建完后,对于每个点
查询时在树上 DFS。运用第七点和第八点综合判定即可。单次复杂度
总复杂度
下面是巨丑考场代码。有微调。
#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;
}
省选救命题