HNOI2016D1T3 树 题解

    xiaoxiao2021-03-25  97

    (题目描述略)

    纯数据结构题,不管是树分块也好,还是树套树也罢。基本思想是将每一次复制操作中新增的所有点看作一个块,这样加上初始的模板树也看作一个块,若复制了 m 次则将大树分成 m + 1 块。将大树看作是 m + 1 个节点构成的树,每个节点代表一个块,相邻节点间距离为相应块中深度最小节点的深度差。每次求两个节点间距离时,将两个节点所在块的编号求出,然后将路径分为块内路径和块间路径,分别解决求和即可。

    首先对模板树深度优先搜索,完成相应的倍增求最近公共祖先的预处理。对于第 i 次将模板树中以 fr[i] 为根的子树复制到大树中 to[i] 节点下的操作,通过记录每次复制后大树的总节点数,二分求出 to[i] 节点所在块编号 to1[i],用求区间第 k 大的方法求出 to[i] 节点在模板树中的编号 to0[i],此处需维护一个划分树或主席树。然后,在块 to1[i] 和块 i 间建一条从 to1[i] 指向 i 的,权值为模板树中编号为 fr[to1[i]] 和 to0[i] 间深度差加一的有向边。在 m 次复制操作完成后,对 m + 1 个块构成的树深度优先搜索,完成相应的倍增求最近公共祖先的预处理。

    对于大树中编号为 u 和编号为 v 的节点间距离的查询,首先二分求出 u 和 v 节点所在块编号 u1 和 v1,并求出其分别在块内距离相应块的根的距离。而后,通过倍增数组将 u1 和 v1 上移至同一深度,再一同上移,同时记录路径权值和,直至到达以下状态之一: 状态 1:u1 和 v1 块的父节点相同,且 u1 和 v1 块节点不相同。将权值和加上模板树中编号为 to0[u1] 和 to0[v1] 节点间的距离即为答案,而后者可以通过求模板树中两节点的最近公共祖先而得到。 状态 2:u1 块的父节点为 v1 块,或 v1 块的父节点为 u1 块。不失一般性的,不妨令 u1 块的父节点为 v1 块,则必有 v1 节点块无上移过程(否则为状态 1),求出大树中编号为 v 节点在模板树中的编号 v0,则将权值和加上模板树中编号为 to0[u1] 和 v0 节点间的距离即为答案。 状态 3:u1 块和 v1 块相同。此时必有 u1 和 v1 节点块无上移过程(否则若有一个节点块存在上移过程则为状态 2,若两个节点块均存在上移过程则为状态 1),求出大树中编号为 u 和 v 节点在模板树中的编号 u0 和 v0,则模板树中编号为 u0 和 v0 节点间的距离即为答案。

    由于数据规模较大,加上此算法常数较大,故需加上输入输出优化此算法才能勉强通过测评。由于复制过程大树节点数可能超过 integer 整型范围,故在必要的地方数值需用 long long integer 超长整型表示。

    代码如下:

    #include"stdio.h" #include"string.h" #define MAX_N (100005) #define Exchange(a,b,type) \ { \ type __tmp_a=a; \ a=b,b=__tmp_a; \ } #define Read(x) \ { \ char __tmp_c=getchar();x=0; \ while(__tmp_c<'0'||__tmp_c>'9') \ __tmp_c=getchar(); \ while(__tmp_c>='0'&&__tmp_c<='9') \ x=x*10+__tmp_c-'0', \ __tmp_c=getchar(); \ } char str[25]; int dep0[MAX_N],dep1[MAX_N],fedg0[MAX_N],fedg1[MAX_N],ft[MAX_N],_index=0,m,prev0[MAX_N][20],prev1[MAX_N][20],st[MAX_N]; long long dist[MAX_N]; struct E { int next,to; }edge0[MAX_N<<1],edge1[MAX_N]; struct PARTITION_TREE { int sum[20][MAX_N],val[20][MAX_N]; void build(int f,int l,int r) { int m=(l+r+1)/2,pl=l,pr=(l+r+1)/2; for(int i=l;i<r;i++) { sum[f][i]=i>l?sum[f][i-1]:0; if(m>val[f][i]) val[f+1][pl++]=val[f][i],++sum[f][i]; else val[f+1][pr++]=val[f][i]; } if(l+1<m) build(f+1,l,m); if(m+1<r) build(f+1,m,r); } int query(int f,int l,int r,int ql,int qr,int p) { if(ql==qr) return val[f][ql]; int m=(l+r+1)/2,lt=l<ql?sum[f][ql-1]:0; if(p<=sum[f][qr]-lt) return query(f+1,l,m,l+lt,l+sum[f][qr]-1,p); else return query(f+1,m,r,m+ql-l-lt,m+qr-l-sum[f][qr],p-sum[f][qr]+lt); } }partree; struct COPY { int fr,to0,to1; long long sum,to; }maintain[MAX_N]; void dfs0(int now,int fah) { st[now]=++_index; partree.val[0][_index]=now; dep0[now]=dep0[fah]+1,prev0[now][0]=fah; for(int i=0;prev0[now][i]>-1;i++) prev0[now][i+1]=prev0[prev0[now][i]][i]; for(int i=fedg0[now];i>-1;i=edge0[i].next) if(edge0[i].to!=fah) dfs0(edge0[i].to,now); ft[now]=_index; } int biquery(int rt,long long p) { int lt=0,md; while(lt+1<rt) { md=(lt+rt)/2; if(maintain[md].sum>=p) rt=md; else lt=md; } return rt; } void dfs1(int now,int fah) { dep1[now]=dep1[fah]+1,prev1[now][0]=fah; dist[now]=dist[fah]+dep0[maintain[now].to0]-dep0[maintain[fah].fr]+1; for(int i=0;prev1[now][i]>-1;i++) prev1[now][i+1]=prev1[prev1[now][i]][i]; for(int i=fedg1[now];i>-1;i=edge1[i].next) dfs1(edge1[i].to,now); } int query0(int u,int v) { int res=dep0[u]+dep0[v]; if(dep0[u]<dep0[v]) Exchange(u,v,int); for(int i=19;i>=0&&dep0[u]>dep0[v];i--) if(1<<i<=dep0[u]-dep0[v]) u=prev0[u][i]; for(int i=19;i>=0&&u!=v;i--) if(prev0[u][i]!=prev0[v][i]) u=prev0[u][i],v=prev0[v][i]; if(u!=v) u=prev0[u][0],v=prev0[v][0]; return res-2*dep0[u]; } long long query1(long long fr,long long to) { int fr0,to0,u=biquery(m+1,fr),v=biquery(m+1,to); fr0=partree.query(0,1,_index+1,st[maintain[u].fr],ft[maintain[u].fr],(int)(fr-maintain[u-1].sum)); to0=partree.query(0,1,_index+1,st[maintain[v].fr],ft[maintain[v].fr],(int)(to-maintain[v-1].sum)); long long res=dep0[fr0]-dep0[maintain[u].fr]+dep0[to0]-dep0[maintain[v].fr]+dist[u]+dist[v]; if(dep1[u]<dep1[v]) { Exchange(fr,to,long long); Exchange(fr0,to0,int); Exchange(u,v,int); } for(int i=19;i>=0&&dep1[u]>dep1[v];i--) if(1<<i<dep1[u]-dep1[v]) u=prev1[u][i]; if(dep1[u]==dep1[v]+1&&prev1[u][0]!=v) u=prev1[u][0]; if(dep1[u]==dep1[v]) for(int i=19;i>=0&&u!=v;i--) if(prev1[u][i]!=prev1[v][i]) u=prev1[u][i],v=prev1[v][i]; res-=dist[u]+dist[v]-2; if(u!=v&&dep1[u]==dep1[v]) fr0=maintain[u].to0,to0=maintain[v].to0; else if(u!=v) fr0=maintain[u].to0,res-=dep0[to0]-dep0[maintain[v].fr]+1; else res-=dep0[fr0]-dep0[maintain[u].fr]+dep0[to0]-dep0[maintain[v].fr]+2; res+=query0(fr0,to0); return res; } void Write(long long x) { if(x==0) putchar('0'); for(str[0]=0;x>0;x/=10) str[++str[0]]=x%10+'0'; while(str[0]>0) putchar(str[str[0]--]); } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); int q;long long fr,to; Read(maintain[1].sum);Read(m);Read(q); memset(fedg0,-1,sizeof(fedg0)); for(int i=1;i<maintain[1].sum;i++) { Read(edge0[i<<1].to);Read(edge0[i<<1^1].to); edge0[i<<1].next=fedg0[edge0[i<<1^1].to]; edge0[i<<1^1].next=fedg0[edge0[i<<1].to]; fedg0[edge0[i<<1^1].to]=i<<1; fedg0[edge0[i<<1].to]=i<<1^1; } memset(prev0,-1,sizeof(prev0)); dep0[0]=-1,dfs0(1,0); partree.build(0,1,_index+1); maintain[1].fr=1,maintain[0].sum=maintain[1].to=maintain[1].to0=maintain[1].to1=0; memset(fedg1,-1,sizeof(fedg1)); for(int i=2;i<=m+1;i++) { Read(maintain[i].fr);Read(maintain[i].to); maintain[i].to1=biquery(i,maintain[i].to); maintain[i].to0=partree.query(0,1,_index+1,st[maintain[maintain[i].to1].fr],ft[maintain[maintain[i].to1].fr],maintain[i].to-maintain[maintain[i].to1-1].sum); maintain[i].sum=maintain[i-1].sum+ft[maintain[i].fr]-st[maintain[i].fr]+1; edge1[i].next=fedg1[maintain[i].to1],edge1[i].to=i; fedg1[maintain[i].to1]=i; } memset(prev1,-1,sizeof(prev1)); dep1[0]=dist[0]=-1,dfs1(1,0); while(q--) { Read(fr);Read(to); Write(query1(fr,to));putchar('\n'); } return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-25203.html

    最新回复(0)