BZOJ4317Atm的树 动态树分治+二分+线段树
Posted CQzhangyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ4317Atm的树 动态树分治+二分+线段树相关的知识,希望对你有一定的参考价值。
【BZOJ4317】Atm的树
Description
Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree……
于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的点有一个距离(什么是距离不用说吧),他需要对于每个点回答:从这个点出发的第k小距离是多少;
如果atm不能回答出来,那么明天4019的闹钟将不会响,4019全寝可能就迟到了,所以atm希望你帮帮他。
Input
第一行,两个正整数n,k,表示树的点数,询问的是第几小距离;
第二~n行,每行三个正整数x,y,w,表示x和y之间有一条边,x为父亲,边权为w;
Output
n行, 每行一个数,第i行输出从i开始第k小距离
Sample Input
5 2
1 5 2
1 2 4
2 3 6
2 4 5
1 5 2
1 2 4
2 3 6
2 4 5
Sample Output
4
5
10
9
6
5
10
9
6
HINT
100% n<=15000, 边权在1~10之间,为了方便,保证1为根;K<=5000
题解:依旧是动态点分治。
统计第k大不太好搞,我们对于每个点都二分一下,变成求到一个点距离<=mid的点有多少个,然后就变成熟悉的题了。
我们对于每个点维护一棵线段树,记录它在点分树的子树中有多少个点到它的距离为x,同时为了去重,还要维护一个从它父亲中减去的版本。
时间复杂度$O(nlog_n^3)$。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; const int maxn=15010; int n,m,N,tot,cnt,rt,mn; int to[maxn<<1],next[maxn<<1],head[maxn],val[maxn<<1],siz[maxn],fa[maxn],md[20][maxn<<1],pos[maxn],vis[maxn]; int Log[maxn<<1],dep[maxn],r1[maxn],r2[maxn]; struct sag { int ls,rs,siz; }s[maxn*100]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } void getrt(int x,int fa) { int tmp=0,i; siz[x]=1; for(i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]&&to[i]!=fa) getrt(to[i],x),tmp=max(tmp,siz[to[i]]),siz[x]+=siz[to[i]]; tmp=max(tmp,tot-siz[x]); if(tmp<mn) mn=tmp,rt=x; } void dfs(int x) { pos[x]=++pos[0],md[0][pos[0]]=dep[x]; for(int i=head[x];i!=-1;i=next[i]) if(!dep[to[i]]) dep[to[i]]=dep[x]+val[i],dfs(to[i]),md[0][++pos[0]]=dep[x]; } void solve(int x) { vis[x]=1; for(int i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]) tot=siz[to[i]],mn=1<<30,getrt(to[i],x),fa[rt]=x,solve(rt); } inline int dis(int x,int y) { int a=pos[x],b=pos[y]; if(a>b) swap(a,b); int k=Log[b-a+1]; return dep[x]+dep[y]-2*min(md[k][a],md[k][b-(1<<k)+1]); } void insert(int l,int r,int &x,int a) { if(!x) x=++tot; s[x].siz++; if(l==r) return ; int mid=(l+r)>>1; if(a<=mid) insert(l,mid,s[x].ls,a); else insert(mid+1,r,s[x].rs,a); } int query(int l,int r,int x,int a,int b) { if(a>b) return 0; if(!x||(a<=l&&r<=b)) return s[x].siz; int mid=(l+r)>>1; if(b<=mid) return query(l,mid,s[x].ls,a,b); if(a>mid) return query(mid+1,r,s[x].rs,a,b); return query(l,mid,s[x].ls,a,b)+query(mid+1,r,s[x].rs,a,b); } inline int calc(int x,int mid) { int ret=0,y,z; for(y=x;y;y=z) { z=fa[y]; if(z) ret-=query(0,N,r2[y],0,mid-dis(x,z)); ret+=query(0,N,r1[y],0,mid-dis(x,y)); } return ret; } int main() { n=rd(),m=rd()+1; int i,j,u,v,a,b,c,l,r,mid; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c),N+=c; dep[1]=1,dfs(1),tot=n,mn=1<<30,getrt(1,0),solve(rt); for(i=2;i<(n<<1);i++) Log[i]=Log[i>>1]+1; for(j=1;(1<<j)<(n<<1);j++) for(i=1;i+(1<<j)-1<(n<<1);i++) md[j][i]=min(md[j-1][i],md[j-1][i+(1<<(j-1))]); for(i=1;i<=n;i++) { for(u=i;u;u=v) { v=fa[u]; if(v) insert(0,N,r2[u],dis(i,v)); insert(0,N,r1[u],dis(i,u)); } } for(i=1;i<=n;i++) { l=0,r=N+1; while(l<r) { mid=(l+r)>>1; if(calc(i,mid)>=m) r=mid; else l=mid+1; } printf("%d\n",r); } return 0; }
以上是关于BZOJ4317Atm的树 动态树分治+二分+线段树的主要内容,如果未能解决你的问题,请参考以下文章