Description
Input
第一行有一个整数 N,表示 S 国城市的数量。
接下来有 N-1 行,每行两个数 u,v 表示一条道路。
第 N+1 行为一个整数 Q,表示接下来有 Q 个操作。
接下来有 Q 行,每行表示一个操作,格式如题目描述所示。
Output
对于每一个 QUERY 操作,输出一个数,表示询问的当前编号为 X 和编号为 Y 的城市的最短路径间的城市 (包括编号为 X 的城市和编号为 Y 的城市)的士兵总和。
Sample Input
Sample Input1:
5
1 2
1 3
3 4
3 5
6
ADD 1 5 2
ADD 3 4 1
QUERY 1 4
SHIFT 1 4
ADD 5 4 1
QUERY 4 5
Sample Input12:
5
1 2
2 3
3 4
4 5
5
ADD 1 3 2
ADD 3 5 1
SHIFT 2 4
QUERY 1 3
QUERY 1 5
Solution
GDOI必有一题数据结构防AK题
此题有两种解法
第一种
(我不是用的这种)
这种方法思考和实现起来比较简单,细节也比较少,但是码量比第二种大,大概5000~6000Byte
用树链剖分和splay来维护,对于每一个重链和每一个轻边建一颗splay
add和query很简单,用常规的树链剖分做法做就行了
shift操作在两颗splay中间时要注意,把前面的最后一个点接到后面的第一个点之前,再把最前面和最后面的splay维护一下即可
第二种
(我用的是这种方法)
用LCT维护
shift操作时要断点和连点,所以splay会被破坏,所以要两棵LCT
一棵是编号LCT,一棵是权值LCT
在makeroot,access等时,编号LCT的编号是固定的,可直接使用
在access的时候,splay的形态会改变,同时要保证两棵LCT形态完全相同
在add,shift,query时,找到编号LCT中某个点对应在权值LCT中的点,然后求值
那么怎样找编号LCT中某个点对应的权值LCT中的点呢?
需要记录下编号LCT中的每棵splay的根对应的权值LCT中的每棵splay的根是哪一个,反过来也需要记录下来,这个只用在access和rotate的时候维护
因为两棵LCT形态相同,所以中序遍历的点是相同的
所有找对应点时,利用编号LCT中点的位置,通过中序遍历找到权值LCT对应的点
然后就愉快的码吧
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 101000
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
using namespace std;
int p[N],fa[2][N],t[2][N][2],rev[2][N],n,root[2][N],s[N];
ll lz[N],sum[N],d[N],size[2][N];
int lr(int qq,int x){return x==t[qq][fa[qq][x]][1];}
void down(int qq,int x)
{
if(rev[qq][x])
{
swap(t[qq][x][0],t[qq][x][1]);
rev[qq][t[qq][x][0]]^=1;rev[qq][t[qq][x][1]]^=1;
rev[qq][x]=0;
}
if(qq==1)
{
if(lz[x])
{
if(t[1][x][0]) d[t[1][x][0]]+=lz[x],sum[t[1][x][0]]+=lz[x]*size[1][t[1][x][0]],lz[t[1][x][0]]+=lz[x];
if(t[1][x][1]) d[t[1][x][1]]+=lz[x],sum[t[1][x][1]]+=lz[x]*size[1][t[1][x][1]],lz[t[1][x][1]]+=lz[x];
lz[x]=0;
}
}
}
int kth(int r,int x)
{
down(1,r);
if(x==size[1][t[1][r][0]]+1) return r;
if(x<=size[1][t[1][r][0]]) return kth(t[1][r][0],x);
else return kth(t[1][r][1],x-size[1][t[1][r][0]]-1);
}
void update(int qq,int x)
{
size[qq][x]=size[qq][t[qq][x][0]]+size[qq][t[qq][x][1]]+1;
if(qq==1) sum[x]=sum[t[1][x][0]]+sum[t[1][x][1]]+d[x];
}
void rotate(int qq,int x)
{
int y=fa[qq][x],k=lr(qq,x);
t[qq][y][k]=t[qq][x][1-k];if(t[qq][x][1-k]) fa[qq][t[qq][x][1-k]]=y;
fa[qq][x]=fa[qq][y];
if(fa[qq][y]) t[qq][fa[qq][y]][lr(qq,y)]=x;
else
{
root[qq][x]=root[qq][y];root[1-qq][root[qq][y]]=x;
if(qq==0) p[x]=p[y],p[y]=0;
}
fa[qq][y]=x;t[qq][x][1-k]=y;
update(qq,y);update(qq,x);
}
void xc(int qq,int x)
{
for(;x;x=fa[qq][x]) s[++s[0]]=x;
for(;s[0];s[0]--) down(qq,s[s[0]]);
}
void splay(int qq,int x,int y)
{
xc(qq,x);
while(fa[qq][x]!=y)
{
if(fa[qq][fa[qq][x]]!=y)
if(lr(qq,x)==lr(qq,fa[qq][x])) rotate(qq,fa[qq][x]);
else rotate(qq,x);
rotate(qq,x);
}
}
int get(int x)
{
splay(1,root[0][x],0);
return kth(root[0][x],size[0][t[0][x][0]]+1);
}
void access(int x)
{
int y=0,yy=0;
while(x>0)
{
splay(0,x,0);
int xx=get(x);
splay(1,xx,0);
fa[0][t[0][x][1]]=0;p[t[0][x][1]]=x;
fa[1][t[1][xx][1]]=0;
root[0][t[0][x][1]]=t[1][xx][1];
root[1][t[1][xx][1]]=t[0][x][1];
t[0][x][1]=y;
t[1][xx][1]=yy;
fa[0][y]=x;p[y]=0;
fa[1][yy]=xx;
update(0,x);update(1,xx);
y=x;x=p[x];yy=xx;
}
}
void makeroot(int x)
{
access(x);
splay(0,x,0);
rev[0][x]^=1;
int xx=get(x);
splay(1,xx,0);
rev[1][xx]^=1;
}
void link(int x,int y)
{
makeroot(x);p[x]=y;
}
int main()
{
freopen("shift.in","r",stdin);
freopen("shift.out","w",stdout);
scanf("%d",&n);
fo(i,1,n) root[0][i]=root[1][i]=i;
fo(i,1,n-1)
{
int x,y;scanf("%d%d",&x,&y);
link(x,y);
}
int ac;scanf("%d\n",&ac);
for(;ac;ac--)
{
char ch;scanf("%c",&ch);
int x,y;ll z;
if(ch=='A')
{
scanf("DD%d%d%d\n",&x,&y,&z);
makeroot(x);
access(y);
splay(0,x,0);
int x1=get(x);
splay(1,x1,0);
d[x1]+=z;sum[x1]+=z*size[1][x1];lz[x1]+=z;
}
if(ch=='S')
{
scanf("HIFT%d%d\n",&x,&y);
if(x==y) continue;
makeroot(x);access(y);
splay(0,x,0);
int xx=get(x);
splay(0,y,0);
int yy=get(y);
splay(1,yy,0);fa[1][t[1][yy][0]]=0,t[1][yy][0]=0;
splay(1,xx,0);fa[1][t[1][yy][1]=xx]=yy,root[0][y]=yy;
}
if(ch=='Q')
{
scanf("UERY%d%d\n",&x,&y);
makeroot(x);access(y);
splay(0,x,0);
int x1=get(x);
splay(1,x1,0);
printf("%lld\n",sum[x1]);
}
}
}