[loj6868]种苹果
在树上随机选\(K\)个点,并建立虚树,称虚树上的点为关键点
结论:一个点到关键点最近距离的期望为\(O(\frac{n}{K})\)
将所有点按到其距离排序,最坏情况即链的端点,此时结论是经典的
对于后两种操作,可以将两个点不断移动到父亲,直至两者相同或位于关键点
将相邻关键点间的链看作整块,每条边看作散块,即类似序列上的问题
维护整块权值的有序数组:整块修改懒标记、散块修改归并、整块查询二分、散块查询暴力
对于前两种操作,可能会影响结论中的性质,因此需要每\(D\)次操作重构,单次重构为\(O(n\log n)\)
时间复杂度为\(O(mK\log n+m(\frac{n}{K}+D)+\frac{nm}{D}\log n)\)
取\(\begin{cases}K=\sqrt{\frac{n}{\log n}}\\D=\sqrt{n\log n}\end{cases}\)时取到最小,时间复杂度为\(O(m\sqrt{n\log n})\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vi;
const int N=400005;
int n,m,t,K,D,p,x,y,z,ans,fa[N],id[N],vis[N],bl[N],Fa[N],Vis[N];
ll a[N],tag[N];
vi vx,vy,v0,v1,e[N],v[N];
mt19937 rng(0);
void dfs(int k,int f){
fa[k]=f;
for(int i:e[k])
if (i!=f)dfs(i,k);
}
void dfs1(int k){
int s=0;
for(int i:e[k]){
dfs1(i);
if (bl[i])s++;
}
if (s>1)vis[k]=1;
if (vis[k])bl[k]=++t;
else{
vis[k]=bl[k]=0;
for(int i:e[k])bl[k]|=bl[i];
}
if (bl[k])v[bl[k]].push_back(k);
}
void dfs2(int k,int lst){
if (vis[k])Fa[k]=lst,lst=k;
for(int i:e[k])dfs2(i,lst);
}
bool cmp(int x,int y){
return a[x]<a[y];
}
void build(){
D=(int)sqrt(n*log(n+1));
for(int i=1;i<=n;i++)e[i].clear();
for(int i=2;i<=n;i++)e[fa[i]].push_back(i);
for(int i=1;i<=n;i++)id[i]=i;
shuffle(id+1,id+n+1,rng);
memset(vis,0,sizeof(vis));
vis[1]=1;
for(int i=1;i<=K;i++)vis[id[i]]=1;
for(int i=1;i<=t;i++){
for(int j:v[i])a[j]+=tag[i];
tag[i]=0,v[i].clear();
}
t=0,dfs1(1),dfs2(1,0);
for(int i=1;i<=t;i++)sort(v[i].begin(),v[i].end(),cmp);
}
void get_link(int x,int y){
v0.clear(),v1.clear();
int x0=x,y0=y;
while (!vis[x0])x0=fa[x0];
while (!vis[y0])y0=fa[y0];
for(int i=x0;i;i=Fa[i])vis[i]=-1;
int k=y0;
while (vis[k]>0)k=Fa[k];
for(int i=x0;i;i=Fa[i])vis[i]=1;
if (k!=x0){
while (x!=x0)v1.push_back(x),x=fa[x];
while (Fa[x]!=k)v0.push_back(bl[x]),x=Fa[x];
}
if (k!=y0){
while (y!=y0)v1.push_back(y),y=fa[y];
while (Fa[y]!=k)v0.push_back(bl[y]),y=Fa[y];
}
for(int i=x;i!=k;i=fa[i])vis[i]-=2;
vis[k]-=2;
while (vis[y]>=0)v1.push_back(y),y=fa[y];
for(int i=x;i!=k;i=fa[i])vis[i]+=2;
vis[k]+=2;
while (x!=y)v1.push_back(x),x=fa[x];
v1.push_back(x);
}
void upd(int k){
vx.clear(),vy.clear();
for(int i:v[k]){
if (vis[i]<0)vx.push_back(i);
else vy.push_back(i);
}
int sx=vx.size(),sy=vy.size();
for(int i=0,j=0;(i<sx)||(j<sy);){
if ((i<sx)&&((j==sy)||(cmp(vx[i],vy[j]))))v[k][i+j]=vx[i],i++;
else v[k][i+j]=vy[j],j++;
}
}
int query(int k,int x){
a[0]=x-tag[k];
return v[k].end()-lower_bound(v[k].begin(),v[k].end(),0,cmp);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<n;i++){
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
K=(int)sqrt(n/log(n+1));
dfs(1,0),build();
for(int i=1;i<=m;i++){
scanf("%d",&p);
if (p==1){
scanf("%d%d%lld",&x,&y,&a[++n]);
x^=ans,y^=ans,a[n]^=ans;
if (fa[x]==y)swap(x,y);
fa[n]=x,fa[y]=n;
if (bl[y]){
a[n]-=tag[bl[y]],bl[n]=bl[y];
v[bl[n]].push_back(n);
vis[n]=-1,upd(bl[n]),vis[n]=0;
}
if (--D==0)build();
}
if (p==2){
n++;
scanf("%d%lld",&fa[n],&a[n]);
fa[n]^=ans,a[n]^=ans;
if (--D==0)build();
}
if (p==3){
scanf("%d%d%d",&x,&y,&z);
x^=ans,y^=ans,z^=ans;
get_link(x,y);
for(int i:v0)tag[i]+=z;
for(int i:v1)a[i]+=z,vis[i]-=2;
for(int i:v1)
if ((bl[i])&&(!Vis[bl[i]]))Vis[bl[i]]=1,upd(bl[i]);
for(int i:v1){
vis[i]+=2;
if (bl[i])Vis[bl[i]]=0;
}
}
if (p==4){
scanf("%d%d%d",&x,&y,&z);
x^=ans,y^=ans,z^=ans;
ans=0,get_link(x,y);
for(int i:v0)ans+=query(i,z);
for(int i:v1)ans+=(a[i]+tag[bl[i]]>=z);
printf("%d\n",ans);
}
}
return 0;
}