wanxue

博客园 首页 新随笔 联系 订阅 管理

题目大意

现在有个有向图图,共有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");
    }
}
posted on 2024-08-08 22:45  thelatersnow  阅读(12)  评论(0编辑  收藏  举报