LGP8819 [CSP-S 2022] 星战 笔记

原题地址:传送门

题意简述

给定一个 \(N\) 个结点,\(M\) 条边的有向图。有 \(Q\) 次操作,分为 \(4\) 种:

  • \(\textrm{opt}=1\) 失活一条边
  • \(\textrm{opt}=2\) 失活以 \(u\) 为终点的所有边
  • \(\textrm{opt}=3\) 激活一条边
  • \(\textrm{opt}=4\) 激活以 \(u\) 为终点的所有边

每次操作之后,都需要判断原图是否是一个内向基环树森林(注:每个点出度都为 \(1\) 的有向弱连通图即为内向基环树)。

\(N,M,Q\le 5\times 10^5\)

解决方案

我们发现对于操作一和操作三我们可以 \(O(1)\) 完成修改,但是对于操作二和操作四我们只能 \(O(N)\) 地做。出度不好维护,如果维护入度呢?现在四个操作都是 \(O(1)\) 的了。但是,如何通过对入度的判定解决原问题呢?

记结点 \(u\) 的入度为 \(r_u\)。显然,每有一条来自 \(v\) 的边连向 \(u\),就会给 \(r_u\) 增加 \(1\),而我们希望判定每个 \(v\) 只有一条边连出去。可现在的 \(r_u\) 对于任何结点连过来的边都只是加一,这样无法标识边的来源——我们想到,能不能通过某种手段标识出边的来源呢?

此时哈希登场。我们给每个结点随机一个权值 \(w_u\),现在,每有一条来自 \(v\) 的边连向 \(u\) 时,我们给 \(r_u\) 加上 \(w_v\)。记每个结点的出度为 \(k_u\),那么 \(\sum r_u=\sum k_u\times w_u\)。我们现在只用判定 \(\sum r_u=\sum w_u\) 成立与否就行了。由于哈希值域大且取值随机的特性,几乎只有当 \(\forall k_u=1\) 时上式成立。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long lolo;
const int MaxN=5e5+5;
mt19937 mrd(time(0));
int N,M,W[MaxN],X,Y,Q,Opt;
lolo tsum,csum,fis[MaxN],cis[MaxN];
int main(){
    scanf("%d%d",&N,&M);
    for(int i = 1;i <= N;i++)W[i]=mrd(),tsum+=W[i];
    for(int i = 1;i <= M;i++){
        scanf("%d%d",&X,&Y);
        fis[Y]+=W[X],csum+=W[X];
    }
    scanf("%d",&Q);memcpy(cis,fis,sizeof(fis));
    for(int i = 1;i <= Q;i++){
        scanf("%d%d",&Opt,&X);
        if(Opt==1)scanf("%d",&Y),cis[Y]-=W[X],csum-=W[X];
        if(Opt==2)csum-=cis[X],cis[X]=0;
        if(Opt==3)scanf("%d",&Y),cis[Y]+=W[X],csum+=W[X];
        if(Opt==4)csum+=fis[X]-cis[X],cis[X]=fis[X];
        csum==tsum?puts("YES"):puts("NO");
    }
    return 0;
}

反思与总结

在这题中,我们先观察操作的时间复杂度,把对出度的维护转化为对入度的维护;再通过哈希的手段给不同元素(不同结点连出来的边)以不同的值标识,并计算它们的和(\(\sum r_u\))与标准的和(\(\sum w_u\))比较,通过哈希的性质保证这个关于元素出现次数(\(k_u\))的方程几乎只有唯一解。这种哈希可以被称为“和哈希”。

posted @ 2025-01-08 14:54  矞龙OrinLoong  阅读(51)  评论(0)    收藏  举报