LCT
看见树边可以删除和加入,就会直接想到用LCT,但是如何用LCT维护如题所述的边的信息呢?
考虑一个询问(u,v),如果他是合法的,那么所有(共价大爷走的)链都一定经过它。换一个角度,也就是说所有链的两个端点都一定分居这条边两侧!这提醒可以我们从端点入手,而不是边。
如何判断所有所有链的两个端点是否分居两侧?用异或值!我们对于每一个加入的链,在链的两端点异或上一个随机大整数。对于询问(u,v),假设v的深度较大,如果v的子树的异或值能够等于所有链的总异或值,那么我们就可以认为它被所有链穿过了。
于是我们只要用LCT维护子树即可,具体就是维护两个值val和sum,详见代码。
#include<cstdio>
#include<algorithm>
#define ll long long
#define N 100005
#define M 300005
using namespace std;
int lcnt, n, m;
struct Line
{
int x, y;
ll v;
}line[M];
struct node *null;
struct node
{
node *fa, *ch[2];
ll val, sum;
//val = 自己的值 + 所有虚儿子的sum
//sum = 自己的值 + 所有实儿子的sum + 所有虚儿子的sum
bool rev;
void modify(ll v)
{
val ^= v;
sum ^= v;
}
void modify_v(ll v)
{
access();
splay();
val ^= v;
sum ^= v;
}
void push_up()
{
sum = val ^ ch[0]->sum ^ ch[1]->sum;
}
void push_down()
{
if(!rev)return;
if(ch[0] != null)ch[0]->do_rev();
if(ch[1] != null)ch[1]->do_rev();
rev = 0;
}
int type()
{
return fa->ch[0] == this ? 0 : 1;
}
bool isroot()
{
return fa->ch[type()] != this;
}
void do_rev()
{
rev^=1;
node *tmp = ch[0];
ch[0] = ch[1];
ch[1] = tmp;
}
void rotate()
{
node *gfa = fa->fa, *ffa = fa;
int f1 = type(), f2 = fa->type();
if(!fa->isroot())
gfa->ch[f2] = this;
node *c = ch[f1^1];
if(c != null)c->fa=ffa;
this->fa=gfa;
this->ch[f1^1]=ffa;
ffa->fa=this;
ffa->ch[f1]=c;
ffa->push_up();
}
void pre()
{
if(!isroot())fa->pre();
push_down();
}
void splay()
{
pre();
while(!isroot())
{
if(!fa->isroot())
type() == fa->type() ? fa->rotate() : rotate();
rotate();
}
push_up();
}
void access()
{
node *tmp = null, *p = this;
for(; p!=null; tmp = p, p = p->fa)
{
p->splay();
p->modify(p->ch[1]->sum ^ tmp->sum);
p->ch[1] = tmp;
p->push_up();
}
}
void make_root()
{
access();
splay();
do_rev();
}
void link(node *p)
{
make_root();
fa = p;
p->modify_v(sum);
}
void cut(node *p)
{
make_root();
p->access();
p->splay();
p->ch[0] = fa = null;
p->push_up();
}
}t[N];
void link(int a, int b){t[a].link(&t[b]);}
void cut(int a, int b){t[a].cut(&t[b]);}
void init()
{
null = new node;
null -> fa = null;
null -> ch[0] = null -> ch[1] =null;
node normal = (node){null,null,null,0,0};
for(int i = 1; i <= n; i++)
t[i] = normal;
}
ll Random()
{
return 1ll*rand()*rand()*rand()*rand();
}
int main()
{
ll all = 0;
scanf("%d",&n);
scanf("%d%d",&n,&m);
init();
for(int i = 1, a, b; i < n; i++)
{
scanf("%d%d",&a,&b);
link(a,b);
}
while(m--)
{
int opt;
scanf("%d",&opt);
if(opt == 1)
{
int x, y, u, v;
scanf("%d%d%d%d",&x,&y,&u,&v);
cut(x,y);
link(u,v);
}
else if(opt == 2)
{
int x, y;
ll v;
scanf("%d%d",&x,&y);
line[++lcnt]=(Line){x,y,v = Random()};
all ^= v;
t[x].modify_v(v);
t[y].modify_v(v);
}
else if(opt == 3)
{
int timer;
scanf("%d",&timer);
all ^= line[timer].v;
t[line[timer].x].modify_v(line[timer].v);
t[line[timer].y].modify_v(line[timer].v);
}
else
{
int x, y;
scanf("%d%d",&x,&y);
t[x].make_root();
t[y].access();
t[x].splay();
if(t[y].sum == all)
puts("YES");
else puts("NO");
}
}
}