Problem
Input
Hint
Solution
(首先义正辞严地指摘一下出数据的人,竟然出这么水的数据)这道题一眼链剖,对吧。但是反转操作不太好搞。于是我就打了个暴力,(本来也可以满分)但是打错了,样例都过不去,就没交。
正解也即链剖。但是由于要维护子树信息的反转操作,我们必须以一种比较奇特的方式建树。
首先我们发现数据很小,所以递归dfs也不会爆栈。那么我们就愉快地dfs一遍,求出每个点的重儿子;然后再愉快地dfs一遍,按照链剖的套路,按重儿子优先的顺序给每个点重标号,然后求出每个重标号后的点的深度、父亲节点、重链链顶。
但是由于要维护子树信息,我们还必须求出每个重标号后的点x的子树中标号最大的点tail[x]。这样,由于每个点的新标号肯定比它的子节点小,而且定然是连续的,我们就可以确保x到tail[x]恰为x的子树。而且为了后面便于反转,我用了一种极不寻常的建树方式:我没有按照套路,将每条重链都建成一棵线段树;而是将一整棵树建成一棵线段树。这样一来,我反转时,一次修改便可修改到许多不同的重链上的信息。
而且,由于本题有多种颜色,且每种颜色的信息不得混淆,我就为每种颜色各开一棵线段树(反正至多10种颜色),也即开10个root,记录一段区间内这种颜色的点的权值和。举个栗子,假若点3为0色,点4为2色,则2色的线段树中[3,4]这个区间就只加上点4的权值,而不加上点3的权值。
这样的话,直接分治一遍不好建树,我就一个一个点地插进线段树中。
预处理完了,我们就必须直面那些操作。
对于反转操作(u,x,y),我们可以从root[x]和root[y]出发,从区间[1,n]搜区间[u,tail[u]](就像普通的线段树区间修改一样),每搜到一个完整的区间,我们就使这两个区间的编号交换。这就有两种情况:1.u为1,那么整棵树都会被反转,所以root被交换,那么就相当于整棵树被反转;2.非root节点被交换,那么其实就是它们的父亲节点的那个子节点交换了,即相当于以它们为根的子树被反转。
对于询问操作,我们就像普通的链剖那样,只不过要注意对应的颜色。
对于修改操作(u,c,val),我不能像普通的链剖那样修改,因为我的建树方式不支持我这样做。所以我是搜一遍每种颜色,然后各以
O(log2n)
O
(
l
o
g
2
n
)
的时间看一看在这种颜色的树中,[u,u]这段区间权值和v是否不为0:找到了则在那棵树中的u位置插一个权值-v;没找到则说明u那个点的权值本身即为0,不用管它。不管找没找到,我们都要在c颜色的那棵树中的u位置插一个权值val。
时间复杂度:
O((n+qlog2n)log2n)
O
(
(
n
+
q
l
o
g
2
n
)
l
o
g
2
n
)
。
Code
#include <cstdio>
#include <algorithm>
using namespace std;
#define N 50001
#define M 2*N
#define S N*40
#define dx dfn[x]
#define dy dfn[y]
#define A(v) son[v][0]
#define B(v) son[v][1]
#define hu top[u]
#define hv top[v]
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fs for(int i=last[x];i;i=next[i])
int i,n,C[N],V[N],x,y,tot,tov[M],next[M],last[N],fat[N],size[N],ps[N],dfn[N],num[N],fa[N],
deep[N],top[N],tail[N],root[10],f[S],son[S][2],m,u,v,c,val,ans;
char s[6];
inline void insert(int x,int y)
{
tov[++tot]=y;
next[tot]=last[x];
last[x]=tot;
}
void dfs(int x)
{
size[x]=1;
int y;
fs
{
y=tov[i];
if(y==fat[x])continue;
fat[y]=x;
dfs(y);
size[x]+=size[y];
if(size[y]>size[ps[x]])ps[x]=y;
}
}
void dfs1(int x)
{
int y;
if(y=ps[x])
{
num[dy=++tot]=y;
deep[dy]=deep[dx]+1;
top[dy]=top[dx];
dfs1(y);
fa[dy]=dx;
tail[dx]=tail[dy];
}
else
{
tail[dx]=dx;
return;
}
fs
{
y=tov[i];
if(y==fat[x]||y==ps[x])continue;
num[dy=++tot]=y;
deep[dy]=deep[dx]+1;
top[dy]=dy;
dfs1(y);
fa[dy]=dx;
tail[dx]=tail[dy];
}
}
inline int&R(int&x)
{
return x=x?x:++tot;
}
void ins(int v,int l,int r,int x,int val)
{
f[v]+=val;
if(l==r)return;
int mid=(l+r)/2;
if(x<=mid)
ins(R(A(v)),l,mid,x,val);
else ins(R(B(v)),mid+1,r,x,val);
}
void maketree()
{
fo(i,1,n)ins(root[C[i]],1,n,dfn[i],V[i]);
}
void init()
{
dfs(1);
dfs1(dfn[1]=num[1]=top[1]=tot=1);
tot=0;
fo(i,0,9)R(root[i]);
maketree();
}
inline void update(int v)
{
f[v]=f[R(A(v))]+f[R(B(v))];
}
void change(int&vx,int&vy,int l,int r,int x,int y)
{
if(x<=l&&r<=y)
{
swap(vx,vy);
return;
}
int mid=(l+r)/2;
if(x<=mid)change(R(A(vx)),R(A(vy)),l,mid,x,y);
if(y>mid)change(R(B(vx)),R(B(vy)),mid+1,r,x,y);
update(vx);update(vy);
}
int find(int v,int l,int r,int x,int y)
{
if(x<=l&&r<=y)return f[v];
int mid=(l+r)/2,ans=0;
if(x<=mid&&A(v))ans=find(A(v),l,mid,x,y);
if(y>mid&&B(v))ans+=find(B(v),mid+1,r,x,y);
return ans;
}
int access()
{
int ans=0;
while(hu!=hv)
{
if(deep[hu]<deep[hv])swap(u,v);
ans+=find(root[c],1,n,hu,u);
u=fa[hu];
}
if(u>v)swap(u,v);
return ans+find(root[c],1,n,u,v);
}
void modify()
{
int i,v;
fo(i,0,9)
if(v=find(root[i],1,n,u,u))
{
ins(root[i],1,n,u,-v);
break;
}
ins(root[c],1,n,u,val);
}
int main()
{
scanf("%d",&n);
fo(i,1,n)scanf("%d",&C[i]);
fo(i,1,n)scanf("%d",&V[i]);
fo(i,1,n-1)
{
scanf("%d%d",&x,&y);
x++;y++;
insert(x,y);
insert(y,x);
}
init();
scanf("%d",&m);
fo(i,1,m)
{
scanf("%s",&s);
switch(s[0])
{
case 'C':
{
scanf("%d%d%d",&u,&x,&y);
u=dfn[u+1];
change(root[x],root[y],1,n,u,tail[u]);
break;
}
case 'A':
{
scanf("%d%d%d",&u,&v,&c);
u=dfn[u+1];v=dfn[v+1];
ans=access();
printf("%d\n",ans);
break;
}
case 'S':
{
scanf("%d%d%d",&u,&c,&val);
u=dfn[u+1];
modify();
break;
}
}
}
}