bzoj 4539 [Hnoi2016]树——主席树+倍增

时间:2023-03-08 20:37:18

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4539

明明就是把每次复制的一个子树当作一个点,这样能连出一个树的结构,自己竟然都没想到。思维有待加强。

找编号为 k 的点,可以通过给 dfs 序建立对于编号的主席树。可以做一个 s[ i ] 表示第 i 步操作之后一共有多少个点,二分得知编号第 k 大的点在哪一步操作建出的大点里,然后用主席树查一下具体是哪个小点即可。每个大点记录一下自己的根,还有连向父亲中的哪个小点。

处理出每个小点在原树种的倍增数组和每个大点在新树中的倍增数组,就能查距离了。每个询问用刚才的二分和主席树找到询问的是哪个大点中的哪个小点。

在大点上倍增的时候先别跳最后一步,看看跳了之后是不是在同一个大点里,如果是,直接查询小点之间的距离即可。

数组开 n*17 好像有些不够。 n*20 可以。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ls Ls[cr]
#define rs Rs[cr]
#define ll long long
using namespace std;
ll rdn()
{
ll ret=;bool fx=;char ch=getchar();
while(ch>''||ch<''){if(ch=='-')fx=;ch=getchar();}
while(ch>=''&&ch<='')ret=ret*+ch-'',ch=getchar();
return fx?ret:-ret;
}
const int N=1e5+,K=,M=2e6+;//,M=N*K;
int n,m,Q,hd[N],xnt,to[N<<],nxt[N<<],pre[N][K+],dep[N],siz[N];
int h2[N],xt2,t2[N<<],nt2[N<<],pr2[N][K+],dp2[N];
int tim,dfn[N],rt[N],tot,Ls[M],Rs[M],sm[M];
ll s[N],dis[N][K+]; int bin[K+];
struct Node{
int x,y;
Node(int x=,int y=):x(x),y(y) {}
}a[N];
void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
void ad2(int x,int y){t2[++xt2]=y;nt2[xt2]=h2[x];h2[x]=xt2;}
void ins(int l,int r,int &cr,int pr,int k)
{
cr=++tot; ls=Ls[pr]; rs=Rs[pr]; sm[cr]=sm[pr]+;
if(l==r)return; int mid=l+r>>;
if(k<=mid)ins(l,mid,ls,Ls[pr],k);
else ins(mid+,r,rs,Rs[pr],k);
}
int qry(int l,int r,int cr,int pr,int k)
{
if(l==r)return l; int mid=l+r>>;
int s=sm[ls]-sm[Ls[pr]];
if(s>=k)return qry(l,mid,ls,Ls[pr],k);
else return qry(mid+,r,rs,Rs[pr],k-s);
}
void ini_dfs(int cr,int fa)
{
siz[cr]=; dep[cr]=dep[fa]+;
dfn[cr]=++tim; ins(,n,rt[tim],rt[tim-],cr);
pre[cr][]=fa;
for(int t=,d;(d=pre[pre[cr][t-]][t-]);t++)
pre[cr][t]=d;
for(int i=hd[cr],v;i;i=nxt[i])
if((v=to[i])!=fa) ini_dfs(v,cr),siz[cr]+=siz[v];
}
int Dis(int x,int y)
{
if(!x||!y)return ; int ret=;//
for(int t=K,lm=dep[x];t>=;t--)
if(dep[pre[y][t]]>=lm)
ret+=bin[t], y=pre[y][t];
return ret;
}
void Ini_dfs(int cr,int fa)
{
dp2[cr]=dp2[fa]+; pr2[cr][]=fa;
dis[cr][]=Dis(a[fa].x,a[cr].y)+;
for(int t=,d;pr2[d=pr2[cr][t-]][t-];t++)
{
pr2[cr][t]=pr2[d][t-];
dis[cr][t]=dis[cr][t-]+dis[d][t-];
}
for(int i=h2[cr],v;i;i=nt2[i])
if((v=t2[i])!=fa) Ini_dfs(v,cr);
}
Node fnd(ll k,int r)
{
int l=,bh=;Node ret;
while(l<=r)
{
int mid=l+r>>;
if(s[mid]>=k)bh=mid,r=mid-;
else l=mid+;
}
ret.x=bh; k-=s[bh-]; bh=a[bh].x;
ret.y=qry(,n,rt[dfn[bh]+siz[bh]-],rt[dfn[bh]-],k);
return ret;
}
int Dis2(int x,int y)
{
int ret=;
if(dep[x]!=dep[y])
{
if(dep[x]<dep[y])swap(x,y);
for(int t=K,lm=dep[y];t>=;t--)
if(dep[pre[x][t]]>=lm)
ret+=bin[t], x=pre[x][t];
}
if(x!=y)
{
for(int t=K;t>=;t--)
if(pre[x][t]!=pre[y][t])
{ ret+=bin[t]<<; x=pre[x][t]; y=pre[y][t];}
ret+=;//
}
return ret;
}
int main()
{
n=rdn();m=rdn();Q=rdn(); ll u,v;
for(int i=;i<n;i++)
u=rdn(),v=rdn(),add(u,v),add(v,u);
bin[]=;for(int i=;i<=K;i++)bin[i]=bin[i-]<<;
ini_dfs(,); tot=;s[]=n;a[]=Node(,); m++;
for(int i=;i<=m;i++)
{
u=rdn();v=rdn();s[i]=s[i-]+siz[u];
Node bh=fnd(v,i-); a[i]=Node(u,bh.y);
ad2(i,bh.x); ad2(bh.x,i);
}
Ini_dfs(,);
for(int i=;i<=Q;i++)
{
u=rdn(); v=rdn(); ll ans=;
Node x=fnd(u,m), y=fnd(v,m);//*.x:blk, *.y:point
if(x.x==y.x){printf("%d\n",Dis2(x.y,y.y));continue;}
if(dp2[x.x]!=dp2[y.x])
{
if(dp2[x.x]<dp2[y.x])swap(x,y);
ans+=Dis(a[x.x].x,x.y);
for(int t=K,lm=dp2[y.x]+;t>=;t--)
if(dp2[pr2[x.x][t]]>=lm)
ans+=dis[x.x][t], x.x=pr2[x.x][t];
if(pr2[x.x][]==y.x)
{printf("%lld\n",ans+Dis2(y.y,a[x.x].y)+);continue;}
ans+=dis[x.x][]; x.x=pr2[x.x][]; x.y=a[x.x].x;
}
ans+=Dis(a[y.x].x,y.y); ans+=Dis(a[x.x].x,x.y);
for(int t=K;t>=;t--)
if(pr2[x.x][t]!=pr2[y.x][t])
{
ans+=dis[x.x][t]+dis[y.x][t];
x.x=pr2[x.x][t]; y.x=pr2[y.x][t];
}
printf("%lld\n",ans+Dis2(a[x.x].y,a[y.x].y)+);
}
return ;
}