LGP8819 [CSP-S 2022] 星战 笔记

原题地址:传送门

题意简述

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

  • opt=1 失活一条边
  • opt=2 失活以 u 为终点的所有边
  • opt=3 激活一条边
  • opt=4 激活以 u 为终点的所有边

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

N,M,Q5×105

解决方案

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

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

此时哈希登场。我们给每个结点随机一个权值 wu,现在,每有一条来自 v 的边连向 u 时,我们给 ru 加上 wv。记每个结点的出度为 ku,那么 ru=ku×wu。我们现在只用判定 ru=wu 成立与否就行了。由于哈希值域大且取值随机的特性,几乎只有当 ku=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;
}

反思与总结

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

posted @   矞龙OrinLoong  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示