P8819 [CSP-S 2022] 星战 题解
“不可以,总司令”
随机化的巧妙运用
考虑什么时候可以发起反攻,
每个节点都可以走到一个环上,每个节点的出度为\(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;
}