困难的图论
题意
给定由\(n\)个点\(m\)条边组成的无向连通图,保证没有重边和自环。
你需要找出所有边,满足这些边恰好存在于一个简单环中。一个环被称为简单环,当且仅当
它包含的所有点都只在这个环中被经过了一次。
注意到这些边可能有很多条,你只需要输出他们编号的异或和即可。
思路
可以先从图中dfs出一棵生成树
可知任一简单环一定包含一条非树边
把玩样例可以发现,若一条边存在于一个简单环中,当且仅当它满足如下两个条件之一:
- 此边为非树边,且它覆盖的树边不和任何其他非树边覆盖的树边有公共边
- 此边为树边,且覆盖它的非树边满足上述条件
于是我们可以对每一个非树边覆盖的树边进行链加,加完后枚举每一条非树边统计答案
具体实现
运用树上差分思想可以算出每条树边被多少条非树边覆盖,我们称它为\(val[i]\)
若此非树边所覆盖的树边的\(val[i]\)均为\(1\),则可以累加
那么如何判断和累加?可以用前缀来做(具体见代码)
(还有一个玄皮的性质:环上的点在生成树上形成一条链,因此不用求\(lca\))
即无向图生成树的非树边只有返祖边
注意事项
- 建双向边
- 注意顺序,不要同一个简单环加两次
不知为什么,交上去总是\(RE\)
最后受不了开挂
int size=40<<20;//40M
__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));//网站开栈
还发现一个有趣的东西:一般关于环的问题都要建出生成树,然后考虑树边与非树边的关系
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+50000;
int n,m,tot,ans;
int fi[N],ne[N<<1],to[N<<1],w[N<<1];
int val[N],dep[N],s[N],asd[N];
bool pd[N];
inline int read()
{
int s=0,w=1; char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
return s*w;
}
inline void add(int x,int y,int s)
{
ne[++tot]=fi[x],fi[x]=tot,to[tot]=y,w[tot]=s;
}
void dfs1(int x,int pr)
{
dep[x]=dep[pr]+1;
pd[x]=true;
for(int i=fi[x];i;i=ne[i])
{
int v=to[i];
if(v==pr) continue;
if(pd[v])
{
if(dep[v]>dep[x]) continue;//顺序问题
++val[x],--val[v];
continue;
}
dfs1(v,x);
}
}
void dfs2(int x,int pr)
{
pd[x]=true;
for(int i=fi[x];i;i=ne[i])
{
int v=to[i];
if(v==pr||pd[v]) continue;
asd[v]=asd[x]^w[i];
dfs2(v,x);
val[x]+=val[v];
}
}
void dfs3(int x,int pr)
{
pd[x]=true;s[x]=s[pr]+val[x];
for(int i=fi[x];i;i=ne[i])
{
int v=to[i];
if(v==pr) continue;
if(pd[v])
{
if(dep[v]>dep[x]) continue;
if((dep[x]-dep[v])==(s[x]-s[v]))
ans^=asd[x]^asd[v]^w[i];
continue;
}
dfs3(v,x);
}
}
int main()
{
int size=40<<20;//40M
__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));
n=read(),m=read();
for(int i=1;i<=m;++i)
{
int a=read(),b=read();
add(a,b,i);add(b,a,i);
}
dfs1(1,0);
for(int i=1;i<=n;++i)pd[i]=false;
dfs2(1,0);
for(int i=1;i<=n;++i)pd[i]=false;
dfs3(1,0);
cout<<ans;
exit(0);
}
NO PAIN NO GAIN