[BZOJ]1758: [Wc2010]重建计划
Posted ditoly
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ]1758: [Wc2010]重建计划相关的知识,希望对你有一定的参考价值。
题目大意:给定一棵n个点的带边权的树和l,u,求长度在[l,u]之间平均权值最大的链的权值。(n<=100,000)
思路:二分答案,把树上每条边减去二分出的答案,点分治check是否有长度在[l,u]之间权值和大等0的链,每次把每棵子树按深度排序,记下各个深度到根距离最大的节点,再用单调队列统计即可,总复杂度O(nlogn^2)。此题卡常差评,重心只要算一次,不用每次二分都算,稍微卡卡就过了。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<‘0‘||c>‘9‘); for(x=c-‘0‘;(c=getchar())>=‘0‘&&c<=‘9‘;)x=(x<<3)+(x<<1)+c-‘0‘; return x; } #define MN 100000 struct edge{int nx,t,w;}e[MN*2+5]; int n,a,b,h[MN+5],en,sz,ms,u[MN+5],s[MN+5],ht[MN+5],d[MN+5]; struct que{int nx,t;}qu[MN+5]; int qh[MN+5],qun,q[MN+5],qn,dq[MN+5],ql,qr,cnt,rt[MN+5]; double w,dis[MN+5],mxd[MN+5]; inline void ins(int x,int y,int w) { e[++en]=(edge){h[x],y,w};h[x]=en; e[++en]=(edge){h[y],x,w};h[y]=en; } void dfs(int x) { s[x]=ht[x]=u[x]=1; for(int i=h[x];i;i=e[i].nx)if(!u[e[i].t]) { dis[e[i].t]=dis[x]+e[i].w-w; d[e[i].t]=d[x]+1; dfs(e[i].t); s[x]+=s[e[i].t]; ht[x]=max(ht[x],ht[e[i].t]+1); } u[x]=0; } void getrt(int x) { int mx=0,i;u[x]=1; for(i=h[x];i;i=e[i].nx)if(!u[e[i].t]) getrt(e[i].t),mx=max(mx,s[e[i].t]); mx=max(mx,sz-s[x]); if(mx<ms)ms=mx,rt[cnt]=x; u[x]=0; } bool solve(int x) { int i,k,l,p=0,o,mx=0; u[x]=1; for(i=h[x];i;i=e[i].nx)if(!u[e[i].t]) { dis[e[i].t]=e[i].w-w; d[e[i].t]=1; dfs(e[i].t); mx=max(mx,ht[e[i].t]); } memset(qh,qun=0,sizeof(int)*(mx+1)); for(i=h[x];i;i=e[i].nx)if(!u[e[i].t]) qu[++qun]=(que){qh[ht[e[i].t]],e[i].t},qh[ht[e[i].t]]=qun; for(i=1;i<=mx;++i) { mxd[i]=-1e18; for(int&j=qh[i];j;p=i,j=qu[j].nx) { for(u[q[k=qn=ql=0]=qu[j].t]=1,qr=-1;k<=qn;++k) { while(p>=0&&d[q[k]]+p>=a) { while(ql<=qr&&mxd[p]>=mxd[dq[qr]])--qr; dq[++qr]=p--; } while(ql<=qr&&d[q[k]]+dq[ql]>b)++ql; if(ql<=qr&&mxd[dq[ql]]+dis[q[k]]>=0)return true; for(o=h[q[k]];o;o=e[o].nx)if(!u[e[o].t])u[q[++qn]=e[o].t]=1; } for(k=0;k<=qn;++k)mxd[d[q[k]]]=max(mxd[d[q[k]]],dis[q[k]]),u[q[k]]=0; } } for(i=h[x];i;i=e[i].nx)if(!u[e[i].t]) { if(!rt[++cnt])sz=ms=s[e[i].t],getrt(e[i].t); if(solve(rt[cnt]))return true; } return false; } bool check(double x) { memset(u,cnt=0,sizeof(u)); w=x;if(!rt[0])dfs(1),sz=ms=n,getrt(1); return solve(rt[0]); } int main() { int i,x,y;double l,r,mid; n=read();a=read();b=read(); for(i=1;i<n;++i)x=read(),y=read(),ins(x,y,read()); for(l=1,r=1000000;r-l>5*1e-4;)(check(mid=(l+r)/2)?l:r)=mid; printf("%.3lf",(l+r)/2); }
以上是关于[BZOJ]1758: [Wc2010]重建计划的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check