[省选联考 2021 A 卷] 支配 题解
lindongli2004 · · 题解
题意本身就已经够简洁的了,所以这里不再复述。
首先建一棵支配树(建法在最下面),那么一个点
那么如果只经过原图上的边,是不可能存在这样的路径的,所以一定要经过新加的有向边
祖先这个限制不好处理,那进一步地,如果可以转化为不经过
所以问题又转化为,如果存在一条路径
复杂度
支配树的建法:
因为
然后类似拓扑排序一样,首先把
-
取出队头
u ,对于所有受支配集中存在点u 的点,把u 从受支配集中删掉。 -
对于所有没有入队并且受支配集中只剩下本身(支配集大小为
1 )的点v 入队,连边(u,v) 。
等队列为空,就可以得到一颗支配树了!
在放代码前,写一写在考场上的心路历程吧。
考场上读完三题,5分钟就想到了这题的大概做法,但是写完之后,对拍不断出错,于是我就陷入了出错,调试,出错,调试,出错...... 如此这样的死循环。
2.5h的时候,我绝望了,面对
但我想,反正也进不了队了,豁出去了!我抖擞精神,想了一会儿 T1,T2 大概能拿
最后因为 T3 常数太大了,没通过民间数据,只拿到了
最后是稍作修(ka)改(chang)后的考场代码(要是没通过的话我会回来修锅的啊哈哈):
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3021;
int n,m,tot,tot2,tot1,Did,Tid,ans;
int v[N],v1[N],v2[N],stk[N*N],cnt[N],siz[N],fa[N],dfn[N],vis1[N],cf[N],vis2[N];
bool del[N],vis[N],dis[N][N],init[N],cg[N][N];
struct Edge{int to,next;}e[N<<3],e1[N<<3],e2[N<<3];
void add1(int x,int y){
e1[++tot1].to=y; e1[tot1].next=v1[x]; v1[x]=tot1;
}
void add2(int x,int y){
e2[++tot2].to=y; e2[tot2].next=v2[x]; v2[x]=tot2;
}
void add(int x,int y){
// cerr<<"add "<<x<<" -> "<<y<<endl;
e[++tot].to=y; e[tot].next=v[x]; v[x]=tot;
}
void dfs(int x){
if(del[x])return; vis[x]=1;
for(int p=v1[x];p;p=e1[p].next)
if(!vis[e1[p].to])dfs(e1[p].to);
}
void dfs2(int x){
if(del[x])return; vis[x]=1;
for(int p=v2[x];p;p=e2[p].next)
if(!vis[e2[p].to])dfs2(e2[p].to);
}
void dfs1(int x){
siz[x]=1; dfn[x]=++Did;
for(int p=v[x];p;p=e[p].next){
int kp=e[p].to; fa[kp]=x;
dfs1(kp); siz[x]+=siz[kp];
}
}
bool Init(int x,int y){
// cerr<<"Init "<<x<<" "<<y<<" "<<dfnendl;
return (dfn[y]>=dfn[x] && dfn[y]<dfn[x]+siz[x]);
}
void getans(int x,int y){
// if(ctt==3)cerr<<"getans "<<x<<endl;
vis1[x]=vis2[x]=Tid;
if(!Init(fa[x],y) && vis2[fa[x]]!=Tid)stk[++ans]=x;
for(int p=v1[x];p;p=e1[p].next)
if(vis1[e1[p].to]!=Tid)getans(e1[p].to,y);
vis2[x]=Tid-1;
}
bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
int read(){
int x=0,f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
// freopen("dominator.in","r",stdin);
// freopen("dominator.out","w",stdout);
n=read(); m=read(); int aq=read();
for(int x,y,i=1;i<=m;i++)
x=read(),y=read(),add1(x,y),add2(y,x);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
del[j]=vis[j]=0; del[i]=1;
dfs(1);
for(int j=1;j<=n;j++)
if(!vis[j])dis[j][i]=1,++cnt[j];
}
// for(int i=1;i<=n;i++)cout<<cnt[i]<<" ";cout<<endl;
// for(int i=1;i<=n;i++,cout<<endl)
// for(int j=1;j<=n;j++)cout<<dis[i][j]<<" ";
int top; stk[top=1]=1; init[1]=1;
for(int i=1;i<=top;i++){
// cout<<"cnt[3]="<<cnt[3]<<" i="<<i<<endl;
// if(i>n)break;
int w=stk[i];
// cout<<"w="<<w<<endl;
// if(i==60){cout<<w<<endl;for(int j=1;j<=n;j++)cout<<cnt[j]<<" ";cout<<endl;}
for(int j=1;j<=n;j++)
if(dis[j][w])--cnt[j];
for(int j=1;j<=n;j++)
if(cnt[j]==1 && !init[j])
init[j]=1,add(w,j),stk[++top]=j;
// if(i==60){for(int j=1;j<=n;j++)cout<<cnt[j]<<" ";cout<<endl;}
}
fa[1]=1; dfs1(1);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
del[j]=vis[j]=0;
del[fa[i]]=1; dfs2(i);
for(int j=1;j<=n;j++)
cg[i][j]=vis[j];
}
// cerr<<"fa:";for(int i=1;i<=n;i++)cerr<<fa[i]<<" ";cerr<<endl;
// cout<<"siz:";for(int i=1;i<=n;i++)cout<<siz[i]<<" ";cout<<endl;
// int ctt=0;
while(aq--){
int x=read(),y=read(),x1=x;
ans=0; ++Tid;
while(x1!=1)vis1[x1]=Tid,x1=fa[x1]; vis1[1]=Tid;
for(int i=1;i<=n+1;i++)cf[i]=0;
// getans(y,x);
for(int i=1;i<=n;i++)
if(cg[i][y] && vis1[fa[i]]!=Tid)
++cf[dfn[i]],--cf[dfn[i]+siz[i]];
for(int i=1;i<=n;i++)
cf[i]+=cf[i-1],ans+=(cf[i]!=0);
// sort(stk+1,stk+ans+1,cmp);
// if(ctt==5){
// cerr<<"stk:";for(int i=1;i<=ans;i++)cerr<<stk[i]<<" ";cerr<<endl;
// }
// int mxr=0,ct=0;
// for(int i=1;i<=ans;i++){
// if(dfn[stk[i]]<=mxr)continue;
// mxr=dfn[stk[i]]+siz[stk[i]]-1; ct+=siz[stk[i]];
// }
// cout<<x<<" -> "<<y<<endl;
printf("%d\n",ans);
}
return 0;
}
彩蛋?大样例是用脚造的,上面的代码保留了大部分我考场艰辛的调试信息,删掉调试信息也就