“不可以,总司令”
题目传送门
随机化的巧妙运用
考虑什么时候可以发起反攻,
每个节点都可以走到一个环上,每个节点的出度为\(1\) .....
事实上,我们会发现第一个条件是没用的,因为当每个节点出度为一时就一定可以走到一个环上
所以这个问题就转化为了判断当前的图是否每个点的出度为 \(1\)
但是我们发现如果维护每个点的出度的话复杂度会假掉,修复/炸掉一个节点是要遍历他的所有出度,不行。
既然维护出度不行,难道我们维护入度吗?
还真是,维护入度可以在 O(1) 的时间里实现
但是维护入度有一个问题,
每个节点入度为 \(1\) 仅仅只是 每个结点的出度唯一的 必要条件
那我们就要想办法将这个必要条件尽可能转化为 充要条件 ,也就是说要让每个节点给其他节点带来的入度变得特殊
所以,我们最终会想到给每个节点附上一个随机权值,看最终所有入度的权值之和是否等于每个节点出度为 \(1\) 时的入度权值之和
然后这题就做完了
上代码!
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
mt19937 rnd;
int n, m,q;
struct edge
{int f, t;
};
edge eds[1000010];
struct node
{int num;int key;vector<int> to;ll tin_;ll in_;//表示这个节点在一开始的入度之和// ll out_;
};
node nod[1000010];
ll tot_quan;//总的权值
ll ans_quan;//所有节点出度为 1 时的权值之和
int main()
{ios::sync_with_stdio(false);srand(time(NULL));rnd.seed(rand());cin >> n >> m;int a, b, c;for (int yy = 1; yy <= n; yy++){nod[yy].key = rnd();//给每个点附上随机权值ans_quan += nod[yy].key;}for (int ww = 1; ww <= m; ww++){cin >> a >> b;eds[ww].f = a;eds[ww].t = b;nod[a].to.push_back(ww);// nod[a].out_ += nod[a].key;nod[b].in_ += nod[a].key;}for (int ww = 1; ww <= n; ww++){tot_quan += nod[ww].in_;nod[ww].tin_=nod[ww].in_;}cin >> q;int t;for (int ww = 1; ww <= q; ww++)//接下来模拟操作即可{cin >> t;if (t == 1){cin>>a>>b;tot_quan-=nod[a].key;nod[b].tin_-=nod[a].key;}else if (t == 2){cin>>a;tot_quan-=nod[a].tin_;nod[a].tin_=0;}else if (t == 3){cin>>a>>b;tot_quan+=nod[a].key;nod[b].tin_+=nod[a].key;}else if (t == 4){cin>>a;tot_quan+=(nod[a].in_-nod[a].tin_);nod[a].tin_=nod[a].in_;}if(tot_quan==ans_quan){cout<<"YES\n";}else{cout<<"NO\n";}}return 0;
}