【bzoj1758】 Wc2010—重建计划
http://www.lydsy.com/JudgeOnline/problem.php?id=1758 (题目链接)
题意
给出一棵树,每条边有边权,问选出一条长度为$[L,U]$的路径,使得路径上的边权平均数最大是多少。
Solution
哈哈,爸爸终于过啦。
首先二分答案,然后路径统计显然点分治,统计答案的时候单调队列维护一下滑动窗口里面的最值。因为要点分治若干次,我们不妨将重心预处理出来,减少常数。
一定要小心,在点分治处理子树的时候,一定要按照深度从小到大的顺序处理,不然直接被新加的那组扫把型的数据卡掉T_T。
细节
清空cnt。
代码
// bzoj1758 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<queue> #define LL long long #define LD long double #define eps 1e-4 #define inf (1ll<<60) #define Pi acos(-1.0) #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout) using namespace std; const int maxn=100010; int n,m,U,L,cnt,head[maxn]; struct edge {int to,next,w;}e[maxn<<1]; struct data { int num,D; friend bool operator < (data a,data b) {return a.D>b.D;} }; namespace NodeDivide { int Dargen,rt,D,maxD,sum,size[maxn],vis[maxn],Sum[maxn],f[maxn],q[maxn],h[maxn]; edge t[maxn]; double ans,deep[maxn],dis[maxn],Dis[maxn]; void link(int u,int v) {t[++cnt]=(edge){v,h[u]};h[u]=cnt;} void dfs(int x,int fa) { size[x]=f[x]=1; for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) { dfs(e[i].to,x); f[x]=max(f[x],size[e[i].to]); size[x]+=size[e[i].to]; } f[x]=max(f[x],sum-size[x]); if (f[x]<f[rt]) rt=x; } void caldeep(int x,int fa,int d) { D=max(D,d); for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) { deep[e[i].to]=deep[x]+e[i].w; caldeep(e[i].to,x,d+1); } } void caldis(int x,int fa,int d) { dis[d]=max(dis[d],deep[x]); for (int i=head[x];i;i=e[i].next) if (e[i].to!=fa && !vis[e[i].to]) caldis(e[i].to,x,d+1); } void solve(int x,double res) { vis[x]=1;maxD=0; priority_queue<data> T; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) { D=0;deep[e[i].to]=e[i].w; caldeep(e[i].to,x,1); T.push((data){e[i].to,D}); } while (!T.empty()) { data tmp=T.top();T.pop(); caldis(tmp.num,x,1); int l=1,r=0,pl=maxD,pr=maxD; for (int i=1;i<=tmp.D;i++) { for (;pl>-1 && pl>=L-i;pl--) { while (l<=r && Dis[q[r]]-q[r]*res<Dis[pl]-pl*res) r--; q[++r]=pl; } for (;pr>-1 && pr>U-i;pr--) while (l<=r && q[l]>=pr) l++; if (pl<pr) ans=max(ans,dis[i]-i*res+Dis[q[l]]-q[l]*res); } maxD=max(maxD,tmp.D); for (int i=1;i<=tmp.D;i++) Dis[i]=max(Dis[i],dis[i]),dis[i]=0; } for (int i=1;i<=maxD;i++) Dis[i]=0; for (int i=h[x];i;i=t[i].next) if (Sum[t[i].to]>=L) solve(t[i].to,res); vis[x]=0; } void caldargen(int x,int fa) { vis[x]=1; if (fa) link(fa,x);else Dargen=x; for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) { sum=size[e[i].to],rt=0; dfs(e[i].to,x); Sum[rt]=sum; caldargen(rt,x); } vis[x]=0; } void Init() { cnt=0; sum=n;f[rt=0]=1<<30; dfs(1,0);Sum[rt]=sum; caldargen(rt,0); } double main(double res) { ans=-inf; solve(Dargen,res); return ans; } } using namespace NodeDivide; void link(int u,int v,int w) { e[++cnt]=(edge){v,head[u],w};head[u]=cnt; e[++cnt]=(edge){u,head[v],w};head[v]=cnt; } int main() { scanf("%d%d%d",&n,&L,&U); for (int u,v,w,i=1;i<n;i++) { scanf("%d%d%d",&u,&v,&w); link(u,v,w); } Init(); double l=0,r=1000000,ans; while (l<=r) { double mid=(l+r)/2; if (main(mid)>=0) l=mid+eps,ans=mid; else r=mid-eps; } printf("%.3lf",ans); return 0; }
This passage is made by MashiroSky.