题目
题目链接:https://codeforces.com/contest/1555/problem/F
\(n\) 个点,有 \(Q\) 次操作。每次操作给出 \(x,y,z\),如果满足在 \(x,y\) 之间加了一条权值为 \(z\) 的边后,不存在任意一个简单环的所有边权值异或和为 \(0\),那么就连上这条边,否则忽略这条边。
对于每一条边输出它是否被连上了。
\(n\leq 3\times 10^5,Q\leq 5\times 10^5,z\in\{0,1\}\)。
思路
考场被卡常了。。我把
stack<int> st;
st.push(x);
for (int y=x;notrt(y);y=fa[y]) st.push(fa[y]);
for (;st.size();st.pop()) pushdown(st.top());
改成了
void down(int x)
{
if (notrt(x)) down(fa[x]);
pushdown(x);
}
就从 3s 跑到了 0.7s??
有一个显而易见的结论:如果加边后存在一条边同时在两个简单环内,那么必然不会加这条边。因为如果两个简单环的异或和都为 \(1\),那么这两个环拼在一起去掉重复边形成的大环的异或和一定为 \(0\)。
所以说我们需要支持加边,路径覆盖,询问路径异或和。采用 LCT 即可。
为了卡常,我赛后参考了 WZYYN 大爷的代码,没有在 LCT 上维护异或和,而是用带权并查集维护异或和。事实上慢的地方并不是那里 /fad。
而且由于每次路径覆盖一定是覆盖之前没有被覆盖的部分,所以不用懒标记,直接暴力覆盖即可。
时间复杂度 \(O(Q\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=800010;
int n,m,cnt,father[N],xors[N];
int find(int x)
{
if (x==father[x]) return x;
int y=father[x]; father[x]=find(y);
xors[x]^=xors[y];
return father[x];
}
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
struct LCT
{
int ch[N][2],fa[N];
bool rev[N],vis[N],flag[N];
bool pos(int x) { return ch[fa[x]][1]==x; }
bool notrt(int x) { return (ch[fa[x]][0]==x) || (ch[fa[x]][1]==x); }
void pushup(int x)
{
flag[x]=flag[ch[x][0]]|flag[ch[x][1]]|vis[x];
}
void pushdown(int x)
{
if (rev[x])
{
int lc=ch[x][0],rc=ch[x][1];
if (lc) swap(ch[lc][0],ch[lc][1]),rev[lc]^=1;
if (rc) swap(ch[rc][0],ch[rc][1]),rev[rc]^=1;
rev[x]=0;
}
}
void rotate(int x)
{
int y=fa[x],z=fa[y],k=pos(x),c=ch[x][k^1];
if (notrt(y)) ch[z][pos(y)]=x; ch[x][k^1]=y; ch[y][k]=c;
if (c) fa[c]=y; fa[y]=x; fa[x]=z;
pushup(y); pushup(x);
}
void down(int x)
{
if (notrt(x)) down(fa[x]);
pushdown(x);
}
void splay(int x)
{
down(x);
for (;notrt(x);rotate(x))
if (notrt(fa[x])) rotate(pos(x)==pos(fa[x])?fa[x]:x);
}
void access(int x)
{
for (int y=0;x;y=x,x=fa[x])
{
splay(x); ch[x][1]=y;
pushup(x);
}
}
void makert(int x)
{
access(x); splay(x);
rev[x]^=1; swap(ch[x][0],ch[x][1]);
}
void link(int x,int y)
{
makert(x); fa[x]=y;
}
void update(int x)
{
if (x>n) vis[x]=1;
if (ch[x][0]) update(ch[x][0]);
if (ch[x][1]) update(ch[x][1]);
pushup(x);
}
}lct;
int main()
{
n=cnt=read(); m=read();
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=m;i++)
{
int x=read(),y=read(),z=read();
if (find(x)!=find(y))
{
int xx=find(x),yy=find(y);
father[xx]=yy; xors[xx]=xors[x]^xors[y]^z;
cnt++; lct.link(x,cnt); lct.link(y,cnt);
cout<<"YES\n";
}
else
{
if (xors[x]^xors[y]==z) { cout<<"NO\n"; continue; }
lct.makert(x); lct.access(y); lct.splay(x);
if (lct.flag[x]) { cout<<"NO\n"; continue; }
lct.update(x);
cout<<"YES\n";
}
}
return 0;
}