题解:AT_arc045_d [ARC045D] みんな仲良し高橋君
另一篇题解在这说啥呢。咋还扯到圆方树上去了,算了写个方便点的。
先考虑单组询问怎么做。
首先遇到
观察一下发生了什么:有一张
不难猜到,这等价于每个连通块的边数都是偶数。证明考虑归纳(是一个小清新构造捏)。
之后,就等价于,给你一个
先用并查集找出所有极大连通块,然后分类讨论:
-
如果有不少于两个奇数大小的连通块,那删啥都会寄。
-
如果没有,这是不可能的,因为总边数都是奇数。
-
刚好一个,那么其它边一定不合法,在这个连通块里面仔细考虑:
因为连通,所以可以找到一个 dfs 树,那么所有非树边都是返祖边。删除了它不会改变连通性,因此这样的边一定都是 ok 的。
接下来看树边。第一种情况,删掉这条边后不会改变连通性,那么也一定 ok,这个可以拿 dep 和 low 来判。
第二种情况,会改变,那么这条边是一个桥,实际上,只需要维护一个子树边数奇偶性就好了。
时间复杂度
#include<bits/stdc++.h>
using namespace std;
int fa[400005],cnt[400005],bh[400005];
int findfather(int x){
return x==fa[x]?x:fa[x]=findfather(fa[x]);
}
int x[200005],y[200005],ok[200005];
vector<pair<int,int>>g[400005];
int vist[400005],dep[400005],low[400005];
int sz[400005],vv[400005];
void dfs1(int x,int la){
vist[x]=1;sz[x]=1;low[x]=dep[x];
for(auto pi:g[x]){
int cu=pi.first,c2=pi.second;
if(c2==la)continue;
if(vist[cu]){
if(dep[cu]>dep[x])continue;
low[x]=min(low[x],dep[cu]);
++sz[x];ok[c2]=1;
continue;
}
dep[cu]=dep[x]+1;vv[cu]=c2;
dfs1(cu,c2);
low[x]=min(low[x],low[cu]);
sz[x]=sz[x]+sz[cu];
}
}
void dfs2(int x){
for(auto pi:g[x]){
int cu=pi.first,c2=pi.second;
if(vv[cu]!=c2)continue;
if(low[cu]<dep[cu]||sz[cu]%2)ok[c2]=1;
dfs2(cu);
}
}
int main(){
int n;
scanf("%d",&n);
int m=2*n+1;
for(int i=1;i<=2*m;++i)fa[i]=i,cnt[i]=0;
for(int i=1;i<=m;++i){
scanf("%d%d",&x[i],&y[i]);
int u=x[i],v=2*n+1+y[i];
int fu=findfather(u),fv=findfather(v);
if(fu!=fv)fa[fu]=fv,cnt[fv]+=cnt[fu];
++cnt[fv];
}
int gs=0,wz=0;
for(int i=1;i<=2*m;++i)if(fa[i]==i){
if(cnt[i]&1)++gs,wz^=i;
}
if(gs==1){
int t=0;
for(int i=1;i<=2*m;++i)if(findfather(i)==wz){
bh[i]=++t;
}
for(int i=1;i<=m;++i)if(fa[x[i]]==wz){
int u=bh[x[i]],v=bh[m+y[i]];
g[u].emplace_back(v,i);
g[v].emplace_back(u,i);
}
dfs1(bh[wz],0);
dfs2(bh[wz]);
}
for(int i=1;i<=m;++i)puts(ok[i]?"OK":"NG");
return 0;
}