题目大意
现在有个有向图图,共有n个点,m条边
总共有四种操作:操作1:将一条边打上标记
操作2:将一个点出发的所有边打上标记
操作3:将一条边移除标记
操作4:将一个点出发的所有边移除标记
打上标记的边视为被移除
每次操作进行一次询问,如果每个点出度都是1,整张图是个强连通图,那么输出"YES",否则输出"NO"
思路分析
首先,每个点出度都为1时,那么边数和点数相同,一定是个强连通图,所以我们只要记录出度即可
但如果用数组记录出度,每次查询O(n),总体时间复杂度O(nq),而n和q都是5e5,必定会TLE
因此,我们必须另辟蹊径
如果直接把所有边的起始点的序号求和,看是否等于点序号的总和,会出现这么一个问题:1+2+3+4+5=1+1+4+4+5
所以,为了保证不出现该类问题,我们需要对数据进行哈希处理,保证等于序号之和的情况只有序号累加。
这就要求数据足够分散,我们可以使用一个大范围的随机进行实现,然后用long long存储
而这个大范围的随机可以用mt19937,使用方法是 mt19937 rnd(一个数字,一般用time(0)保证随机性)初始化,用rand()进行单次随机
接下来就好做了,我们可以用一个mr数组来记录序号乘以出度的初始值,r数组来记录当前序号乘以出度的值,用now记录所有r的总和,sum记录所有节点序号的总和,每次操作对这些变量进行操作即可,然后判断sum是否与now相等
代码实现
#include<bits/stdc++.h>
using namespace std;
int n,m;
long long w[510000],r[510000],mr[510000],sum=0,now=0;
int main()
{
int u,v,q,t;
mt19937 rnd(time(0));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
w[i]=rnd();
sum+=w[i];
}
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
r[v]+=w[u];
mr[v]=r[v];
now+=w[u];
}
scanf("%d",&q);
while(q--){
scanf("%d%d",&t,&u);
if(t==2){
now-=r[u];
r[u]=0;
}
else if(t==4){
now+=mr[u]-r[u];
r[u]=mr[u];
}
else if(t==1){
scanf("%d",&v);
r[v]-=w[u];
now-=w[u];
}
else if(t==3){
scanf("%d",&v);
r[v]+=w[u];
now+=w[u];
}
if(now==sum)printf("YES\n");
else printf("NO\n");
}
}