题目描述
0~100%
暴力不解释
(强烈谴责辣鸡出题人)
真·100%
树剖。
树剖有一个神奇的性质,它可以在满足剖出重链的同时按照dfs序标号
意思就是可以同时做路径问题和子树问题
当然如果要满足这两个性质肯定不能瞎剖
怎么搞dfs序?
其实很简单,每次向下遍历时优先走重边(保证dfs序),遇到链时直接给一条链标号(保证链上编号连续)。
理解很简单,为了保证重链上的点标号连续先走重链,
之后按照重边走,依次处理完这条重链上点的dfs序。
举个栗子
先走重链
再走其它边
如果不这样走,就会在走完重链后出现这样标号混乱的情况
这样正解就很显然了,因为颜色只有10,所以可以建10棵动态开点线段树,代表10种不同的颜色的答案。
再记录一下每种颜色的树根
单点修改就直接搞,把该点所有颜色清空再修改
查询路径就直接树剖
颜色翻转就把两种颜色对应的区间交换(可能有多个)
主要还是树剖很神奇
code
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;
int tr[2000000][3]; //leftson rightson ans
int a[100001][2];
int ls[50001];
int color[50001];
int weight[50001];
int root[11];
int n,Q,i,j,k,l,m,x,y,z,len,s,ans;
char St[20];
int f[50001];
int next[50001];
int top[50001];
int st[50001];
int ed[50001];
int Fa[50001];
int d[50001];
void swap(int &a,int &b) {int c=a;a=b;b=c;}
void up(int t) {tr[t][2]=tr[tr[t][0]][2]+tr[tr[t][1]][2];}
void New(int x,int y)
{
m++;
a[m][0]=y;
a[m][1]=ls[x];
ls[x]=m;
}
void _new(int x,int y) {tr[x][y]=++len;}
void maketree(int t,int l,int r)//建树
{
if (l==r)
return;
_new(t,0),_new(t,1);
int mid=(l+r)/2;
maketree(tr[t][0],l,mid);
maketree(tr[t][1],mid+1,r);
}
void init(int t,int fa)
{
int i,j,k;
j=0,k=0;
f[t]=1;
for (i=ls[t]; i; i=a[i][1])
if (a[i][0]!=fa)
{
Fa[a[i][0]]=t;
d[a[i][0]]=d[t]+1;
init(a[i][0],t);
f[t]+=f[a[i][0]];
if (f[a[i][0]]>j)
{
j=f[a[i][0]];
k=a[i][0];
}
}
next[t]=k;
}
void Init(int t,int fa)//链剖
{
int i,j,k;
if (!st[t])
{
i=t;
while (i)
{
st[i]=++l;
top[i]=t;
i=next[i];
}
}
if (next[t])
Init(next[t],t);
for (i=ls[t]; i; i=a[i][1])
if (a[i][0]!=fa && a[i][0]!=next[t])
Init(a[i][0],t);
ed[t]=l;
}
void change(int t,int l,int r,int x,int s)//Set操作
{
int mid=(l+r)/2;
if (l==r)
{
tr[t][2]=s;
return;
}
if (x<=mid)
change(tr[t][0],l,mid,x,s);
else
change(tr[t][1],mid+1,r,x,s);
up(t);
}
void find(int t,int l,int r,int x,int y)//Ask操作
{
if (x<=l && r<=y)
{
ans+=tr[t][2];
return;
}
int mid=(l+r)/2;
if (x<=mid)
find(tr[t][0],l,mid,x,y);
if (mid<y)
find(tr[t][1],mid+1,r,x,y);
}
void exchange(int t,int T,int l,int r,int x,int y)//Change操作
{
int mid=(l+r)/2;
if (x<=mid)
{
if (x<=l && mid<=y)
swap(tr[t][0],tr[T][0]);
else
exchange(tr[t][0],tr[T][0],l,mid,x,y);
}
if (mid<y)
{
if (x<=mid+1 && r<=y)
swap(tr[t][1],tr[T][1]);
else
exchange(tr[t][1],tr[T][1],mid+1,r,x,y);
}
up(t),up(T);
}
int main()
{
scanf("%d",&n);
fo(i,1,n)
scanf("%d",&color[i]);
fo(i,1,n)
scanf("%d",&weight[i]);
m=0;
fo(i,2,n)
{
scanf("%d%d",&j,&k);
j++,k++;
New(j,k);
New(k,j);
}
len=10;
fo(i,1,10)
maketree(i,1,n),root[i]=i;
d[1]=0;
init(1,0);l=0;
Init(1,0);
fo(i,1,n)
change(color[i]+1,1,n,st[i],weight[i]);
scanf("%d",&Q);
for (;Q;Q--)
{
scanf("%s%d%d%d",St,&x,&y,&z);
switch (St[0])
{
case 'C':
{
x++;
if (x==1)
swap(root[y+1],root[z+1]);//直接换根
else
exchange(root[y+1],root[z+1],1,n,st[x],ed[x]);
break;
}
case 'A':
{
x++,y++;
ans=0;
while (1)
{
if (top[x]!=top[y])
{
if (d[top[x]]<d[top[y]])
swap(x,y);
find(root[z+1],1,n,st[top[x]],st[x]);
x=Fa[top[x]];
}
else
{
if (d[x]>d[y])
swap(x,y);
find(root[z+1],1,n,st[x],st[y]);
break;
}
}
printf("%d\n",ans);
break;
}
case 'S':
{
x++;
fo(i,1,10)//一定要全部清空
change(i,1,n,st[x],0);
change(root[y+1],1,n,st[x],z);
break;
}
}
}
return 0;
}