bzoj 4539: [Hnoi2016]树

开始读错题了,以后一定要好好读题!!!
称原来给的大小为n的树为模板树,最后得到的树为答案树,把一次操作增加的节点看成一块,然后构成的树称为大树。
答案树上两点的距离=两点分别走到所在块的根,在大树上走到两个块的lca,撤销进入块lca后两个点共同走过的路径。
第一步的答案就是找到点到它所在块的根在原树中的距离
第二步,把大树上两点之间的边权定义为答案树上这两个块的根的距离,然后倍增lca求出
第三步,要知道两个点进入lca后具体是走到模板树上的哪个点,分别设为x,y,然后答案减去2*(lca(x,y)在模板树中的深度-lca块的根在模板树中的深度)
然后把需要维护的东西维护出来就好。
写错的地方:
1.大树中的节点个数是m+1,不是n
2.第三步减去的不是lca(x,y)的深度,而是到所在块的根的距离。
   
   
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
 
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md
#define N 100010
using namespace std;
//kai long long
struct yts { int x,t,l,ne;};
struct Tr { int l,r,sz;};
int n,B[N],C[N],K;
ll A[N];
struct Mu
{
int left[N],q[N],sz[N],right[N],v[N],fa[N][20],dep[N],root[N];
yts e[2*N];
Tr tr[20*N];
int dfs_cnt,cnt,num;
void put(int x,int y)
{
num++; e[num].x=x; e[num].t=y;
e[num].ne=v[x]; v[x]=num;
}
void dfs(int x)
{
left[x]=++dfs_cnt; q[dfs_cnt]=x; sz[x]=1;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (y!=fa[x][0])
{
dep[y]=dep[x]+1; fa[y][0]=x;
dfs(y);
sz[x]+=sz[y];
}
}
right[x]=dfs_cnt;
}
void insert(int &i,int pre,int l,int r,int x)
{
i=++cnt; tr[i].sz=tr[pre].sz+1; tr[i].l=tr[pre].l; tr[i].r=tr[pre].r;
if (l==r) return;
int mid=(l+r)>>1;
if (x<=mid) insert(tr[i].l,tr[pre].l,l,mid,x);
else insert(tr[i].r,tr[pre].r,mid+1,r,x);
}
int query(int L,int R,int l,int r,int k)
{
if (l==r) return l;
int mid=(l+r)>>1,sz=tr[tr[R].l].sz-tr[tr[L].l].sz;
if (sz>=k) return query(tr[L].l,tr[R].l,l,mid,k);
else return query(tr[L].r,tr[R].r,mid+1,r,k-sz);
}
void build()
{
for (int j=1;j<=18;j++)
for (int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for (int i=1;i<=n;i++) insert(root[i],root[i-1],1,n,q[i]);
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
int t=dep[x]-dep[y];
for (int i=18;i>=0;i--)
if (t&(1<<i)) x=fa[x][i];
if (x==y) return dep[x];
for (int i=18;i>=0;i--)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return dep[x]-1;
}
int get_point(ll x,int pos)
{
x=x-A[pos]+1;
return query(root[left[B[pos]]-1],root[right[B[pos]]],1,n,x);
}
int dis_to_root(int pos,ll x)
{
x=get_point(x,pos);
return dep[x]-dep[B[pos]];
}
} mu;
 
struct Big
{
yts e[2*N];
int dep[N],pr[N][20],fa[N][20]; ll dis[N][20];
int v[N],num;
void put(int x,int y,int l)
{
num++; e[num].x=x; e[num].t=y; e[num].l=l;
e[num].ne=v[x]; v[x]=num;
}
void dfs(int x)
{
pr[x][0]=C[x];
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (y!=fa[x][0])
{
dep[y]=dep[x]+1; fa[y][0]=x; dis[y][0]=e[i].l;
dfs(y);
}
}
}
void build()
{
for (int j=1;j<=18;j++)
for (int i=1;i<=K;i++)
if (fa[i][j-1])
fa[i][j]=fa[fa[i][j-1]][j-1],dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1],pr[i][j]=pr[fa[i][j-1]][j-1];
}
ll lca(int x,int y,int &px,int &py,int &lca)
{
ll ans=0;
if (dep[x]>dep[y])
{
int t=dep[x]-dep[y];
for (int i=18;i>=0;i--)
if (t&(1<<i)) { px=pr[x][i]; ans+=dis[x][i]; x=fa[x][i];}
}
else
{
int t=dep[y]-dep[x];
for (int i=18;i>=0;i--)
if (t&(1<<i)) { py=pr[y][i]; ans+=dis[y][i]; y=fa[y][i];}
}
if (x==y) { lca=B[x]; return ans;}
for (int i=18;i>=0;i--)
if (fa[x][i]!=fa[y][i])
{
px=pr[x][i]; py=pr[y][i];
ans+=dis[x][i]; ans+=dis[y][i];
x=fa[x][i]; y=fa[y][i];
}
px=pr[x][0]; py=pr[y][0]; lca=B[fa[x][0]];
return ans+dis[x][0]+dis[y][0];
}
} big;
int find(ll x)
{
int l=1,r=K;
while (l!=r)
{
int mid=(l+r+1)>>1;
if (A[mid]<=x) l=mid; else r=mid-1;
}
return l;
}
 
int main()
{
//freopen("data.in","r",stdin); freopen("data.out","w",stdout);
int m,Q;
scanf("%d%d%d",&n,&m,&Q);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
mu.put(x,y); mu.put(y,x);
}
mu.dfs(1); mu.build();
A[1]=1; B[1]=1; C[1]=0;
ll S=n; K=1;
for (int i=1;i<=m;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y); int pos=find(y);
K++; A[K]=S+1; B[K]=x; C[K]=mu.get_point(y,pos);
big.put(pos,K,mu.dis_to_root(pos,y)+1);
S+=mu.sz[x];
}
big.dfs(1); big.build();
for (int i=1;i<=Q;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y);
int posx=find(x),posy=find(y);
int prex=mu.get_point(x,posx),prey=mu.get_point(y,posy),lca;
ll ans=mu.dis_to_root(posx,x)+mu.dis_to_root(posy,y);
ans+=big.lca(posx,posy,prex,prey,lca);
ans-=(mu.lca(prex,prey)-mu.dep[lca])*2;
printf("%lld\n",ans);
}
return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值