bzoj 4539 hnoi2016 树 倍增lca+主席树
2018-02-09 16:55
323 查看
题目:bzoj 4539 hnoi2016 树
为了做这题专门温习了一天的主席树,结果发现主席树在里边并不是主要作用?
主体部分基本一遍写过,不过这题很坑的是有几个地方需要开longlong,因为1e5*1e5能够爆int
读数的时候也得读longlong
思路:一开始我以为是递归似的合并,然后很差异这样树的大小是指数级的,后来才发现一直以一个模板树来复制。。。
这样就很好做了,将每个复制下来的树当做一个节点,大节点之间的边权是每个复制下的树的根节点的距离,而且也不用真的连边,记到倍增数组里就好;很显然这样大节点中到根的距离就是每个小树的根到大树根的距离,然后再加上要求的点到小根的距离就是要求的点到大树根的距离;
这样两个要求的点到根的距离减去二倍lca到根的距离就是所求的值;
因为输入的点很大,也不可能真建这么多点,所以可以每个大节点记录他的根,子树大小,还有之前的大节点共占用了多少节点,这样二分找到大节点,再主席树找到对应模板树的节点就可以了。
细节见代码,不到200行的码子是我这几天写的最恶心的一题。。。虽然主体过的挺顺利,但那个开longlong的坑还是栽了好久。。。趁着脑子还热乎,赶紧给代码多打几行注释。。。
为了做这题专门温习了一天的主席树,结果发现主席树在里边并不是主要作用?
主体部分基本一遍写过,不过这题很坑的是有几个地方需要开longlong,因为1e5*1e5能够爆int
读数的时候也得读longlong
思路:一开始我以为是递归似的合并,然后很差异这样树的大小是指数级的,后来才发现一直以一个模板树来复制。。。
这样就很好做了,将每个复制下来的树当做一个节点,大节点之间的边权是每个复制下的树的根节点的距离,而且也不用真的连边,记到倍增数组里就好;很显然这样大节点中到根的距离就是每个小树的根到大树根的距离,然后再加上要求的点到小根的距离就是要求的点到大树根的距离;
这样两个要求的点到根的距离减去二倍lca到根的距离就是所求的值;
因为输入的点很大,也不可能真建这么多点,所以可以每个大节点记录他的根,子树大小,还有之前的大节点共占用了多少节点,这样二分找到大节点,再主席树找到对应模板树的节点就可以了。
细节见代码,不到200行的码子是我这几天写的最恶心的一题。。。虽然主体过的挺顺利,但那个开longlong的坑还是栽了好久。。。趁着脑子还热乎,赶紧给代码多打几行注释。。。
#include<iostream> #include<algorithm> #include<string> #include<cstring> #include<cstdio> #define LL long long #define random(a,b) (a+rand()%(b-a+1)) const int maxn=100005; int n,m,q; struct asd { int next,to,val; }edge[maxn*4];//其实开2倍应该就行 int etot=0; int dp[maxn*2][20]; LL dpl[maxn*2][20]; int node[maxn]; LL d[maxn*2]; void add(int x,int y,int z) { edge[++etot].next=node[x]; edge[etot].to=y; edge[etot].val=z; node[x]=etot; } int tmp1,tmp2; int root=n+1; int start[maxn],end[maxn]; int rt[maxn]; struct zxs { int l,r,sum; }t[maxn*40]; int ntot=0; int ttot=0; void build(int l,int r,int &x,int y,int pos) { t[++ttot]=t[y],x=ttot,t[x].sum+=1;//一开始我竟然sb地忘写了个等号。。。 if(l==r)return; int mid=(l+r)>>1; if(pos<=mid)build(l,mid,t[x].l,t[y].l,pos); else build(mid+1,r,t[x].r,t[y].r,pos); } void dfs(int x,int fa) { d[x]=d[fa]+1; dp[x][0]=fa;//把小树上的父节点赋给倍增数组 start[x]=++ntot; build(1,n,rt[ntot],rt[ntot-1],x); for(int i=node[x];i;i=edge[i].next) if(edge[i].to!=fa) dfs(edge[i].to,x); end[x]=ntot; } struct qqq { LL l,r;//l到r是这个大节点包含的小节点编号 int rt,nlc,dlc;//rt是这个复制下来的树的根,nlc是大节点编号,dlc是和上一个大节点相接的小节点的编号; qqq() { l=r=rt=nlc=dlc=0; } qqq(LL x,LL y,int z,int z2,int z3) { l=x,r=y,rt=z,nlc=z2,dlc=z3; } }qu[maxn];//包含每个大节点的信息 int qtot=0; struct ppp { int id,lc;//id是其对应的qu ,lc是其在模板树中的编号 ppp(int x,int y) { id=x,lc=y; } }; int query(int l,int r,int x,int y,int pos) { while(l<r) { int mid=(l+r)>>1; int tsum=t[t[y].l].sum-t[t[x].l].sum; if(tsum>=pos)r=mid,x=t[x].l,y=t[y].l; else l=mid+1,x=t[x].r,y=t[y].r,pos-=tsum;//主席树 } return l; } ppp getpos(LL x) { int l=1,r=qtot; while(l<r) { int mid=(l+r)>>1; if(qu[mid].r<x)l=mid+1; else r=mid;//二分x所在的qu } return ppp(l,query(1,n,rt[start[qu[l].rt]-1],rt[end[qu[l].rt]],x-qu[l].l+1)); } LL getdis(int x,int y) { return d[x]-d[y]<0 ? d[y]-d[x] : d[x]-d[y];//计算小节点间的距离 } void insert(LL x,LL y) { ppp tlc=getpos(y); LL size=end[x]-start[x]+1;//新复制的子树的大小 qtot++; qu[qtot]=qqq(qu[qtot-1].r+1,qu[qtot-1].r+size,x,qtot+n,tlc.lc);//需要另加父亲指针 dp[qtot+n][0]=qu[tlc.id].nlc; dpl[qtot+n][0]=getdis(tlc.lc,qu[tlc.id].rt)+1;//求大节点到父亲大节点的距离 d[qtot+n]=d[qu[tlc.id].nlc]+1;//大节点的深度,用于求lca for(int i=1;i<=18;i++) { dp[qtot+n][i]=dp[dp[qtot+n][i-1]][i-1]; dpl[qtot+n][i]=dpl[dp[qtot+n][i-1]][i-1]+dpl[qtot+n][i-1];//倍增 } } void swp(ppp &x,ppp &y) { ppp z=y; y=x; x=z; } int LCA(int x,int y) { if(d[x]<d[y])std::swap(x,y); for(int i=18;i>=0;i--) if(d[dp[x][i]]>=d[y])x=dp[x][i]; if(x==y)return x; for(int i=18;i>=0;i--) if(dp[x][i]!=dp[y][i])x=dp[x][i],y=dp[y][i]; return dp[x][0];//求模板树中两点的lca } LL findlca(ppp x,ppp y)//求总体lca到根的距离 { int tx=qu[x.id].nlc,ty=qu[y.id].nlc; if(tx==ty)return d[LCA(x.lc,y.lc)]-d[qu[y.id].rt]+dpl[ty][18];//如果所求的两点一开始就在同一大节点,就直接返回大节点到根的距离加上两点lca到这个大节点的小根的距离 if(d[tx]<d[ty])swp(x,y),std::swap(tx,ty); for(int i=18;i>=0;i--) { if(d[dp[tx][i]]>d[ty])tx=dp[tx][i];//倍增,需注意深度同步应相差1,不然不知道是从哪个小节点进入的 } if(dp[tx][0]==ty)return d[LCA(qu[tx-n].dlc,y.lc)]-d[qu[y.id].rt]+dpl[ty][18];//如果此时互为大节点中的父子,返回进入点到父节点小根的距离加上父节点到大根的距离 if(d[tx]>d[ty])tx=dp[tx][0];//深度同步 for(int i=18;i>=0;i--) if(dp[tx][i]!=dp[ty][i])tx=dp[tx][i],ty=dp[ty][i]; int tz=dp[ty][0]; return d[LCA(qu[tx-n].dlc,qu[ty-n].dlc)]-d[qu[tz-n].rt]+dpl[tz][18];//最理想化的情况,返回两个进入点的lca到当前lca大节点的小根的距离加上大节点到大根的距离 } LL fix(ppp tlc)//计算大节点到大根的距离 { return getdis(tlc.lc,qu[tlc.id].rt)+dpl[qu[tlc.id].nlc][18]; } LL findans(LL x,LL y) { ppp tlc1=getpos(x); ppp tlc2=getpos(y); LL lca=findlca(tlc1,tlc2); return fix(tlc1)+fix(tlc2)-lca*2;//减去二倍的lca到大根的距离 } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<n;i++) { scanf("%d%d",&tmp1,&tmp2); add(tmp1,tmp2,1); add(tmp2,tmp1,1); } qu[++qtot]=qqq(1ll,(LL)n,1,n+1,0); d[n+1]=0; d[0]=0; dfs(1,0); for(int k=1;k<=18;k++) for(int i=1;i<=n;i++) dp[i][k]=dp[dp[i][k-1]][k-1];//倍增模板树中的节点 LL tp1,tp2; for(int i=1;i<=m;i++) { scanf("%lld%lld",&tp1,&tp2);//不要忘了longlong insert(tp1,tp2); } for(int i=1;i<=q;i++) { scanf("%lld%lld",&tp1,&tp2); printf("%lld\n",findans(tp1,tp2)); } return 0; }
相关文章推荐
- [BZOJ4539][Hnoi2016]树(倍增+LCA+主席树)
- bzoj 4539 [Hnoi2016]树 主席树 倍增lca
- BZOJ 4539: [Hnoi2016]树 [主席树 lca]
- 【BZOJ 4448】 [Scoi2015]情报传递|倍增LCA|主席树
- BZOJ 2588 Count on a tree 主席树+倍增LCA
- 洛谷 P2633 Count on a tree[bzoj2588](倍增lca+主席树)
- [bzoj3123][洛谷P3302] [SDOI2013]森林(树上主席树+倍增lca+启发式合并)
- [BZOJ4539][Hnoi2016]树(dfs序+主席树+lca)
- bzoj 4539: [Hnoi2016]树 主席树&lca
- bzoj 4539: [Hnoi2016]树(缩点+主席树+lca)
- BZOJ4539 [Hnoi2016]树 【倍增 + 主席树】
- 【bzoj3123】[Sdoi2013]森林 倍增LCA+主席树+启发式合并
- Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA
- 【BZOJ3123】[Sdoi2013]森林 主席树+倍增LCA+启发式合并
- 倍增LCA(bzoj 3732: Network)
- bzoj 4556: [Tjoi2016&Heoi2016]字符串 (主席树+二分+后缀数组+ST表||后缀自动机+线段树合并+LCA)
- 【bzoj4242】水壶 BFS+最小生成树+倍增LCA
- BZOJ 4144 Dijkstra+Kruskal+倍增LCA
- [SDOI2011][BZOJ2286] 消耗战|虚树|树型dp|树上倍增LCA
- bzoj 2791 [Poi2012]Rendezvous 倍增lca 基环树