测试地址:Little Devil I
题目大意:有一棵树,每条边一开始为白色,维护两个操作:将路径上所有的边反色(黑白互换),将所有和路径相邻(只有一个端点在路径上)的边反色,并支持询问一条路径上黑色边的数目。
做法:本题需要用到树链剖分。
首先路径反色很模板,就不说了。关键是路径相邻边反色,看上去很不可做,因此我们考虑等价代换。
注意到,一条边只有一个端点在路径上时,它才会被反色,有零个或两个端点的边不受影响,这看上去就非常的像异或了。于是我们可以给路径上每个点打上标记,一条边只有在两个端点的标记总数为奇数时,才表示它在这种情况下被反色。那么在询问时,重链上的边显然可以合并出来(注意要和路径反色的标记一起考虑),而轻边不超过条,在线段树中分别询问这两个点的标记进行处理即可。
于是我们就解决了这一题,时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int T,n,m,tot,first[100010];
int fa[100010]={0},dep[100010]={0},siz[100010]={0};
int son[100010]={0},top[100010],pos[100010],tim;
bool tag[400010][2],cntl[400010][2],cntr[400010];
int ans[400010];
struct edge
{
int v,next;
}e[200010];
bool flag,lastr;
void insert(int a,int b)
{
e[++tot].v=b;
e[tot].next=first[a];
first[a]=tot;
}
void dfs1(int v)
{
dep[v]=dep[fa[v]]+1;
siz[v]=1,son[v]=0;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v])
{
fa[e[i].v]=v;
dfs1(e[i].v);
siz[v]+=siz[e[i].v];
if (siz[e[i].v]>siz[son[v]])
son[v]=e[i].v;
}
}
void dfs2(int v,int t)
{
top[v]=t,pos[v]=++tim;
if (son[v]) dfs2(son[v],t);
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa[v]&&e[i].v!=son[v])
dfs2(e[i].v,e[i].v);
}
void update(int no,int l,int r,bool op)
{
tag[no][op]=!tag[no][op];
if (!op) cntl[no][0]=!cntl[no][0];
else cntl[no][1]=!cntl[no][1],cntr[no]=!cntr[no];
if (!op) ans[no]=r-l-ans[no];
}
void pushdown(int no,int l,int r)
{
int mid=(l+r)>>1;
if (tag[no][0])
{
update(no<<1,l,mid,0);
update(no<<1|1,mid+1,r,0);
}
if (tag[no][1])
{
update(no<<1,l,mid,1);
update(no<<1|1,mid+1,r,1);
}
tag[no][0]=tag[no][1]=0;
}
void pushup(int no)
{
ans[no]=ans[no<<1]+ans[no<<1|1];
cntl[no][0]=cntl[no<<1][0];
cntl[no][1]=cntl[no<<1][1];
cntr[no]=cntr[no<<1|1];
if (cntl[no<<1|1][1]^cntr[no<<1]^cntl[no<<1|1][0])
ans[no]++;
}
void segmodify(int no,int l,int r,int s,int t,bool op)
{
if (l>=s&&r<=t)
{
update(no,l,r,op);
return;
}
int mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) segmodify(no<<1,l,mid,s,t,op);
if (t>mid) segmodify(no<<1|1,mid+1,r,s,t,op);
pushup(no);
}
int segquery(int no,int l,int r,int s,int t)
{
if (l>=s&&r<=t)
{
bool right=0;
if (flag)
{
right^=cntl[no][0]^cntl[no][1];
right^=lastr;
}
else flag=1;
lastr=cntr[no];
return ans[no]+(int)right;
}
int totans=0,mid=(l+r)>>1;
pushdown(no,l,r);
if (s<=mid) totans+=segquery(no<<1,l,mid,s,t);
if (t>mid) totans+=segquery(no<<1|1,mid+1,r,s,t);
return totans;
}
bool querypoint(int no,int l,int r,int x,int op)
{
if (l==r)
{
if (op==0) return cntl[no][0];
if (op==1) return cntl[no][1];
if (op==2) return cntr[no];
}
int mid=(l+r)>>1;
pushdown(no,l,r);
if (x<=mid) return querypoint(no<<1,l,mid,x,op);
else return querypoint(no<<1|1,mid+1,r,x,op);
}
void modify(int a,int b,bool op)
{
while(top[a]!=top[b])
{
if (dep[top[a]]<dep[top[b]])
swap(a,b);
segmodify(1,1,n,pos[top[a]],pos[a],op);
a=fa[top[a]];
}
if (dep[a]>dep[b]) swap(a,b);
if (!op&&a==b) return;
if (!op) segmodify(1,1,n,pos[a]+1,pos[b],op);
else segmodify(1,1,n,pos[a],pos[b],op);
}
int query(int a,int b)
{
int totans=0;
while(top[a]!=top[b])
{
if (dep[top[a]]<dep[top[b]])
swap(a,b);
flag=0;
totans+=segquery(1,1,n,pos[top[a]],pos[a]);
bool right=0;
right^=querypoint(1,1,n,pos[top[a]],0);
right^=querypoint(1,1,n,pos[top[a]],1);
right^=querypoint(1,1,n,pos[fa[top[a]]],2);
if (right) totans++;
a=fa[top[a]];
}
if (dep[a]>dep[b]) swap(a,b);
flag=0;
totans+=segquery(1,1,n,pos[a],pos[b]);
return totans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
memset(first,0,sizeof(first));
tot=0;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b),insert(b,a);
}
dfs1(1);
tim=0;
dfs2(1,1);
memset(tag,0,sizeof(tag));
memset(cntl,0,sizeof(cntl));
memset(cntr,0,sizeof(cntr));
memset(ans,0,sizeof(ans));
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if (op<=2) modify(x,y,op-1);
else printf("%d
",query(x,y));
}
}
return 0;
}