1 条题解

  • 0
    @ 2025-10-16 22:27:35

    题面很长,但是仔细分析的话,这题不难。

    早知道最后一节课就不去致毅馆外面唱K了。

    题意

    先给你一张有向图,每次进行操作:

    1 u v :删除向边 (u,v)(u,v) ,保证它在原图中存在且未被删除。

    2 u :删除所有在原图中存在的有向边 (i,u)(i,u)

    3 u v :恢复向边 (u,v)(u,v) ,保证它在原图中存在且被删除。

    4 u :恢复所有在原图中存在的有向边 (i,u)(i,u)

    每次操作完之后,判断当前的图是否为一个内向基环树森林,也就是所有点的出度为 11

    40 分做法

    按照上述内容模拟即可,复杂度 Θ(nq)\Theta(nq)

    60 分做法

    注意到这样胡乱删除或修改,成为一片内向基环树森林的概率几乎为 00,所以暴力不能过的点全输出 NO

    100 分做法

    注意到题目要求我们完成的操作非常困难,而我们也无法使用数据解构维护每个点的出度。

    回顾一下我们要判定什么:每个点是否只有恰好一条出边。

    我们根本不在乎这个图长什么样,只要每个点只有一条出边即可。所以我们对于每个点 uu,给所有原图中从 uu 出发的边 (u,v)(u,v) 附上一个相同的权值,这个权值随机赋一个数就行了。我们现在的目标就是判断:对于图中每条边 (u,v)(u,v) 权值的和是否等于 i=1nai\sum_{i=1}^n a_i,如果相等,就鉴定为每个点出度为一。

    然后考虑这个和如何修改。1,3 操作直接改。2,4 操作我们维护一个入边累计边权就行了。

    代码

    #include<bits/stdc++.h>
    #define R(x) x=read()
    #define int long long
    using namespace std;
    inline int read() {
    	int x=0,y=1;
    	char c=getchar();
    	while(c<'0'||c>'9') {
    		if(c=='-') y=-1;
    		c=getchar();
    	}
    	while(c>='0'&&c<='9') {
    		x=(x<<3)+(x<<1)+(c^'0');
    		c=getchar();
    	}
    	return x*y;
    }
    mt19937 rd(time(0));
    const int N=500005;
    int n,m,q;
    int a[N],nw,s;
    int ind[N],tot[N];
    signed main() {
    	R(n),R(m);
    	for(int i=1;i<=n;++i){
    		a[i]=rd()%10000000000000;
    		s+=a[i];
    	}
    	while(m--) {
    		int R(x),R(y);
    		nw+=a[x];
    		ind[y]+=a[x];
    		tot[y]+=a[x];
    	}
    	R(q);
    	while(q--){
    		int R(opt),u,v;
    		if(opt==1){
    			R(u),R(v);
    			nw-=a[u];
    			ind[v]-=a[u];
    		}else if(opt==2){
    			R(u);
    			nw-=ind[u];
    			ind[u]=0;
    		}else if(opt==3){
    			R(u),R(v);
    			nw+=a[u];
    			ind[v]+=a[u];
    		}else{
    			R(u);
    			nw+=tot[u]-ind[u];
    			ind[u]=tot[u];
    		}
    		puts(nw==s?"YES":"NO");
    	}
    	return 0;
    }
    
    • 1

    信息

    ID
    480
    时间
    2000ms
    内存
    512MiB
    难度
    10
    标签
    (无)
    递交数
    1
    已通过
    1
    上传者