簡述題意:給一棵n個節點的數和一個n的數組(n的排列),支持兩個操作:1 詢問a[l]~a[r]的所有點到某個點的距離,2 交換a[x]與a[x+1]。
一開始想到了這道題bzoj4012: [HNOI2015]開店,大概看了之後發現是開店的加強版。我想到像開店一樣使用主席樹處理,顯然操作2很容易。然而
他的時間是一個log。大概看了題解,感覺套路~ 畢竟開店的題解大多是log^2。。。
//以下來自翻譯
對於詢問l~r,可以拆成1~l-1,1~r的詢問。想要詢問一個點到一些點的距離和,可以在重心樹(Centroid Tree)上直接跑。然後給出一個可持久化重心樹(Persistent Centroid Tree),用與線段樹類似的思路,維護當前區間的所有點數量 與到重心父親的距離和,每加入一個節點只需要修改它在重心樹上到root的距離。由於重心樹深度是O(logn),所以直接可持久化每次是O(Σ重心樹上兒子數),這顯然是不優的。
然後就有了O(n)把一般樹轉換爲O(n)節點的二叉樹的算法,就是開店中的限制。之後就是簡單的實現了
有一些細節:儘管轉化爲二叉樹,重心樹節點仍可以有3個兒子。可能需要RMQ O(1)求兩點間距離。我的空間炸炸炸。這道題共151個測試點,時限5000ms。
這樣就得到了一個時間O(nlogn),空間O(nlogn)的優秀算法。
由於窩比較弱,如果有分析錯誤請指出。。。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 800005
#define M 15000005
#define ll long long
using namespace std;
int n,q,p,x,y,z,RT,w[N];
int to[N],len[N],nxt[N],fst[N],u[N],l=1;
int To[N],Len[N],Nxt[N],Fst[N],L;
int a[N],b[N],c[N][3],cnt,st[N],ed[N],Cnt;
int Mx[N],sz[N],fa[N];
int ly,e[N],lg[N],pos[N];
int rt[N],s[M][3],used[M],nd;
ll r[N][26],dep[N],f[M],g[M],ans;
void link(int x,int y,int z)
{
to[++l]=y;len[l]=z;nxt[l]=fst[x];fst[x]=l;
to[++l]=x;len[l]=z;nxt[l]=fst[y];fst[y]=l;
}
void Link(int x,int y,int z)
{
To[++L]=y;Len[L]=z;Nxt[L]=Fst[x];Fst[x]=L;
To[++L]=x;Len[L]=z;Nxt[L]=Fst[y];Fst[y]=L;
}
void edg(int x,int l,int r)
{
if (l==r)
{
link(x,a[l],b[l]);
return;
}
int mid=l+r>>1,tmp=++cnt;
edg(tmp,l,mid);
edg(tmp,mid+1,r);
link(x,tmp,0);
}
void dfs(int x,int f)
{
int t=0;
for (int i=Fst[x];i;i=Nxt[i])
if (To[i]!=f)
a[++t]=To[i],b[t]=Len[i];
if (t)
{
int mid=t+1>>1;
edg(x,1,mid);
if (t!=1) edg(x,mid+1,t);
}
for (int i=Fst[x];i;i=Nxt[i])
if (To[i]!=f)
dfs(To[i],x);
}
void Dfs(int x,int f)
{
sz[x]=1;Mx[x]=0;
for (int i=fst[x];i;i=nxt[i])
{
if (!u[i]&&to[i]!=f)
{
Dfs(to[i],x);
sz[x]+=sz[to[i]];
Mx[x]=max(Mx[x],sz[to[i]]);
}
}
}
void Get(int x,int f,int size,int &G)
{
if (max(Mx[G],size-sz[G])>max(Mx[x],size-sz[x])) G=x;
for (int i=fst[x];i;i=nxt[i])
if (!u[i]&&to[i]!=f) Get(to[i],x,size,G);
}
int build(int x)
{
int G=x,sum=0;
Dfs(x,0);
if (sz[x]==1) return x;
Get(x,0,sz[x],G);
for (int i=fst[G];i;i=nxt[i])
if (!u[i])
{
u[i]=u[i^1]=1;
int t=build(to[i]);
c[G][sum++]=t;
fa[t]=G;
}
return G;
}
void DFS(int x,int f)
{
r[pos[x]=++ly][0]=dep[x];
for (int i=fst[x];i;i=nxt[i])
if (to[i]!=f)
{
dep[to[i]]=dep[x]+len[i];
DFS(to[i],x);
r[++ly][0]=dep[x];
}
}
void DFS(int x)
{
st[x]=++Cnt;
for (int i=0;i<3;i++)
if (c[x][i])
DFS(c[x][i]);
ed[x]=Cnt;
}
ll dis(int x,int y)
{
if (!x||!y) return 0;
x=pos[x];y=pos[y];
if (x>y) swap(x,y);
int t=lg[y-x+1];
return r[x][0]+r[y][0]-2*min(r[x][t],r[y-e[t]+1][t]);
}
void mdy(int x,int &y,int p,int q)
{
if (!y) y=++nd;
g[y]=g[x]+1;
f[y]=f[x]+dis(fa[p],q);
used[y]=used[x];
if (p==q) used[y]=1;
for (int i=0;i<3;i++)
if (c[p][i]&&st[q]>=st[c[p][i]]&&st[q]<=ed[c[p][i]])
mdy(s[x][i],s[y][i]=0,c[p][i],q);
else s[y][i]=s[x][i];
}
ll qry(int x,int y,int z)
{
if (!x) return 0;
if (z==y)
{
ll ans=0;
for (int i=0;i<3;i++)
if (c[y][i]) ans+=f[s[x][i]];
return ans;
}
ll ans=used[x]*dis(y,z);
for (int i=0;i<3;i++)
if (c[y][i]&&st[z]>=st[c[y][i]]&&st[z]<=ed[c[y][i]])
ans+=qry(s[x][i],c[y][i],z);
else ans+=f[s[x][i]]+g[s[x][i]]*dis(y,z);
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
for (int i=2;i<=n;i++)
{
scanf("%d%d%d",&x,&y,&z);
Link(x,y,z);
}
cnt=n;
dfs(1,0);//cnt=2n
L=0;
memset(Fst,0,sizeof Fst);
RT=build(1);
DFS(RT);
DFS(1,0);//ly=4n
lg[1]=0;
for (int i=2;i<=ly;i++)
lg[i]=lg[i>>1]+1;
e[0]=1;
for (int i=1;i<=lg[ly];i++)
e[i]=e[i-1]<<1;
for (int i=1;i<=lg[ly];i++)
{
for (int j=1;j<=ly;j++)
r[j][i]=min(r[j][i-1],r[j+e[i-1]][i-1]);
}
for (int i=1,apa=0;i<=n;i++)
{
mdy(rt[i-1],rt[i],RT,w[i]);//nd+=log 2n
}
while(q--)
{
scanf("%d",&p);
if (p==1)
{
scanf("%d%d%d",&x,&y,&z);
x^=ans%(1<<30);
y^=ans%(1<<30);
z^=ans%(1<<30);
printf("%lld\n",ans=qry(rt[y],RT,z)-qry(rt[x-1],RT,z));
}
else
{
scanf("%d",&x);
x^=ans%(1<<30);
mdy(rt[x-1],rt[x],RT,w[x+1]);
swap(w[x],w[x+1]);
}
}
}