csp2022 T3 的条件充要性证明
必要性显然。
当且仅当只有一个从该据点出发的虫洞可用时
考虑我的暴力做法是缩点成若干 DAG,然后对强连通分量点数大于等于 2 的权值赋为 1,其他赋为 0,然后建反图跑拓扑,看看是不是每个点都能被传递到权值 1。
然后你这样做显然很复杂,根据经验,你觉得不够优美,并且猜测没有人会优化!显然是你性质/条件没找对。
考虑先判完充要后,细究我们缩点后的 DAG。
建议自己画个图。
-
入度为 0 的点的强连通分量大小一定为 1。
证明:考虑大于等于 1 的情况成环,那么已经满足环内每个点出度为 1 了,结果还得多一条出来,直接被必要条件判掉了。 -
强连通分量出度为 1 \(\Leftrightarrow\) 强连通分量大小为 1
证明:类上条结论证即可。若有出边,显然被判掉。 -
只有出度为 0 的点的权值为 1
证明:显然出度为 0,没被判掉,所以一定成环。
然后你想 DAG 只有出度为 0 的点权值为 1,接下来建反图,显然以这些点为源点,每个点都能跑得到。若存在孤立强连通分量的话也显然。
哈哈,你这个结论假如猜对的话,随便过 T3。
考虑条件不好做,且等价于边为 \(n\),每个点的度数为奇数。
考虑奇数想到异或,然后你随便做一下就好了。
也就是说,对于一条边 \((x,y)\),就对全局答案异或上 \(v_x\),\(v_x\) 为给 \(x\) 赋的随机权值。
#include <bits/stdc++.h>
//#define int long long
#define pb push_back
using namespace std;
const int N=(int)(5e5+5);
mt19937 RAND(time(0)^1919810);
int n,m,d[N],v[N],f[N],sum[N],del[N],in[N];
signed main() {
cin.tie(0); ios::sync_with_stdio(false);
cin>>n>>m;
int bian=m,val=0,Lim=0;
for(int i=1;i<=n;i++) v[i]=RAND(),Lim^=v[i];
for(int i=1;i<=m;i++) {
int x,y; cin>>x>>y;
++d[x]; val^=v[x]; ++in[y];
sum[y]^=v[x];
}
int q; cin>>q;
while(q--) {
int op; cin>>op;
if(op==1) {
int x,y; cin>>x>>y;
--bian; f[y]^=v[x]; val^=v[x]; ++del[y];
} else if(op==2) {
int x; cin>>x;
int qwq=sum[x]^f[x]; //未
val^=qwq; f[x]=sum[x];
int re=in[x]-del[x];
bian-=re;
del[x]=in[x];
} else if(op==3) {
int x,y; cin>>x>>y;
++bian; f[y]^=v[x]; val^=v[x]; --del[y];
} else if(op==4) {
int x; cin>>x;
bian+=del[x];
val^=f[x]; f[x]=0; del[x]=0;
}
if(val==Lim&&bian==n) cout<<"YES\n";
else cout<<"NO\n";
}
return 0;
}