P4689 [Ynoi2016] 这是我自己的发明

对根为 1 的有点权的树支持如下操作:

  • 换根
  • 给定 x,y,求 usubtree(x)vsubtree(y)[au=av]

n105m5×105ai109


把子树计数转到 dfs 序上,发现这个就是带换根的 P5268 [SNOI2017] 一个简单的询问

先看不换根怎么办。记 {cn} 为 dfs 序上的 {an}f(a,b,c,d)=u[a,b]v[c,d][cu=cv]

差分,记 g(a,b)=xcntx[1,a]cntx[1,b],那么 f(a,b,c,d)=g(b,d)g(b,c1)g(a1,d)+g(a1,c1),拆成四个询问,可以莫队解决。

处理换根:

  • rt=u,则 u 对应区间 [1,n]

  • rt 在子树 u 外,则 u 对应区间 [stu,edu]

  • 否则,令 v 为链 urt 上离 u 最近的点,u 对应区间 [1,stv1][edv+1,n]

发现如果 uv 均对应两端区间,询问量可以到 16m,不是很理想,考虑减少询问数。

  • u,v 均对应一段区间

上面写过了。

  • u 对应一段区间,v 对应两端区间

也就是

f(l1,r1,1,l2)+f(l1,r1,r2,n)

=g(r1,l2)g(l11,l2)g(r1,r21)+g(l11,r21)+g(r1,n)g(l11,n)

h(x)=g(x,n),即 g(r1,l2)g(l11,l2)g(r1,r21)+g(l11,r21)+h(r1)h(l11)h 容易预处理。

发现只用拆 4 个询问。

  • u,v 均对应两端区间

f(1,l1,1,l2)+f(r1,n,1,l2)+f(1,l1,r2,n)+f(r1,n,r2,n)

也能推出来是

g(l1,l2)g(r11,l2)g(l1,r21)+g(r11,r21)+h(n)+h(l1)+h(l2)h(r11)h(r21)

也只用拆 4 个询问。

B=nm,时间复杂度 O(nm)

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 500010
using namespace std;
int read(){
int x=0,w=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*w;
}
int n,m,B,a[N];
int b[N],len;
struct Q{
int l,r,id,fl;
bool operator<(const Q &x)const{
if(l/B!=x.l/B)return l<x.l;
return (l/B)&1?r>x.r:r<x.r;
}
}q[M<<2];int tq;
void ins(int l,int r,int fl){
if(l>r)swap(l,r);
if(l<1||r>n)return;
q[++tq]={l,r,m,fl};
}
vector<int>e[N];
int st[N],ed[N],tim,c[N];
int fa[N][17],dep[N];
int fath(int x,int k){
for(int i=16;~i;i--)
if((k>>i)&1)x=fa[x][i];
return x;
}
void dfs(int u,int f){
st[u]=++tim,c[tim]=a[u];
fa[u][0]=f,dep[u]=dep[f]+1;
for(int i=1;i<17;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
for(int v:e[u])
if(v!=f)dfs(v,u);
ed[u]=tim;
}
void get(int u,int rt,int &l,int &r,int &f){
if(u==rt)return l=1,r=n,f=false,void();
if(st[rt]<st[u]||ed[rt]>ed[u])
return l=st[u],r=ed[u],f=false,void();
int v=fath(rt,dep[rt]-dep[u]-1);
l=st[v]-1,r=ed[v]+1,f=true;
}
int cntl[N],cntr[N],cntn[N];ll sum,h[N],ans[M];
void llht(int x){sum-=cntr[x],cntl[x]--;}
void rrht(int x){sum+=cntl[x],cntr[x]++;}
void lrht(int x){sum+=cntr[x],cntl[x]++;}
void rlht(int x){sum-=cntl[x],cntr[x]--;}
int main(){
n=read();int T=read();
for(int i=1;i<=n;i++)a[i]=b[i]=read();
sort(b+1,b+1+n),len=unique(b+1,b+1+n)-b-1;
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+1+len,a[i])-b;
for(int i=1,u,v;i<n;i++){
u=read(),v=read();
e[u].push_back(v),e[v].push_back(u);
}
dfs(1,0);
for(int i=1;i<=n;i++)cntn[c[i]]++;
for(int i=1;i<=n;i++)
h[i]=h[i-1]+cntn[c[i]];
for(int opt,rt=1,u,v,l1,r1,l2,r2,f1,f2;T;T--){
opt=read();
if(opt==1){rt=read();continue;}
u=read(),v=read(),m++;
get(u,rt,l1,r1,f1),get(v,rt,l2,r2,f2);
if(!f2)swap(u,v),swap(l1,l2),swap(r1,r2),swap(f1,f2);
if(!f1&&!f2)
ins(r1,r2,1),ins(l1-1,r2,-1),ins(r1,l2-1,-1),ins(l1-1,l2-1,1);
else if(!f1){
ans[m]=h[r1]-h[l1-1];
ins(r1,l2,1),ins(l1-1,l2,-1),ins(r1,r2-1,-1),ins(l1-1,r2-1,1);
}
else{
ans[m]=h[n]+h[l2]+h[l1]-h[r1-1]-h[r2-1];
ins(l1,l2,1),ins(r1-1,l2,-1),ins(l1,r2-1,-1),ins(r1-1,r2-1,1);
}
}
B=ceil(n/sqrt(tq));
sort(q+1,q+1+tq);
for(int i=1,l=0,r=0;i<=tq;i++){
while(l>q[i].l)llht(c[l--]);
while(r<q[i].r)rrht(c[++r]);
while(l<q[i].l)lrht(c[++l]);
while(r>q[i].r)rlht(c[r--]);
ans[q[i].id]+=q[i].fl*sum;
}
for(int i=1;i<=m;i++)
printf("%lld\n",ans[i]);
return 0;
}

本文作者:SE の 摆烂窝

本文链接:https://www.cnblogs.com/SError0819/p/18032918

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   SError  阅读(7)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起