蛮神奇的一道题:题面很神奇,思路很神奇,代码很神奇,数据也很神奇。
怎么有赏析语文课文的感觉。
题面很神奇在于:如果已经“实现连续穿梭”,就必然“实现反击”!也就是说,“实现反击”这个条件是废的。每个点只有一条出边,必然构成一个内向基环树,那么每个点必然会绕进一个环内。
思路很神奇在于:你不会想到本题与哈希有关。
不方便直接考虑每个点是否只有一条出边,但可以换个角度思考:这等价于每条边的起点不重不漏地构成 $1\sim n$。将边 $u\rightarrow v$ 的权值定义为 $h[u]$。维护所有边的权值和,如果其等于 $\sum _{i=1}^n h[i]$,就满足要求,可以反攻。
对于操作 2,删除一个点上的所有入边,考虑维护每个点上所有入边的权值和,即可快速完成。
代码很神奇在于:代码是真的短!下面奉上代码:
// Title: 星战
// Source: CSP-S 2022
// Author: Jerrywang
#include <bits/stdc++.h>
#define ll unsigned long long
#define rep(i, s, t) for(int i=s; i<=t; ++i)
#define debug(x) cerr<<#x<<":"<<x<<endl;
const int N=500010;
using namespace std;
mt19937 rnd(time(0));
int n, m; ll h[N], sum[N], ori[N], cur, tot;
int main()
{
#ifdef Jerrywang
freopen("E:/OI/in.txt", "r", stdin);
#endif
scanf("%d%d", &n, &m);
rep(i, 1, n) h[i]=rnd(), tot+=h[i];
rep(i, 1, m)
{
int u, v; scanf("%d%d", &u, &v);
cur+=h[u];
sum[v]+=h[u];
}
rep(i, 1, n) ori[i]=sum[i];
int T; scanf("%d", &T);
while(T--)
{
int o, u, v; scanf("%d%d", &o, &u);
if(o==1)
{
scanf("%d", &v);
cur-=h[u];
sum[v]-=h[u];
}
else if(o==2)
{
cur-=sum[u];
sum[u]=0;
}
else if(o==3)
{
scanf("%d", &v);
cur+=h[u];
sum[v]+=h[u];
}
else
{
cur-=sum[u];
sum[u]=ori[u];
cur+=sum[u];
}
puts(cur==tot?"YES":"NO");
}
return 0;
}