BZOJ 1758: [Wc2010]重建计划
要求平均值最大,二分平均值,判定是否存在一条合法路径权值为正数,点分治+单调队列
看上去没有任何问题,然而其实每次点分的时候要按子树深度从小到大排序然后更新答案
复杂度O(n log ^ 2 n)
这个东西交上去又T了,然后学习了一下分数规划怎么二分,发现了一点神奇的方法,交上去又T了(可能我自己写炸了)........然后把两种拼一起.......跑得飞起
这题T了7发......
#include<cstdio> #include<algorithm> #include<cmath> #define eps 1e-5 using namespace std; int Cnt,root,n,N,L,R,sz[100005],Dep[100005],last[100005],vis[100005],q[100005],F_[100005]; double ANS,cntkey,ans,G[100005],F[100005]; struct node{ int to,next; double val; }e[200005]; struct node1{ int Dep,id; double val; }E[100005]; void add(int a,int b,double c){ e[++Cnt].to=b; e[Cnt].next=last[a]; e[Cnt].val=c; last[a]=Cnt; } void find_root(int x,int fa){ sz[x]=1,F_[x]=0; for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; if (vis[V] || V==fa) continue; find_root(V,x); sz[x]+=sz[V]; F_[x]=max(F_[x],sz[V]); } F_[x]=max(F_[x],N-sz[x]); if (F_[x]<F_[root]) root=x; } void get_maxdis(int x,int fa,int dep,int st){ Dep[st]=max(Dep[st],dep); for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; if (vis[V] || V==fa) continue; get_maxdis(V,x,dep+1,st); } } bool cmp(node1 a,node1 b){ return a.Dep<b.Dep; } void get_dis(int x,int fa,int dep,double val){ sz[x]=1; F[dep]=max(F[dep],val); if (dep>=L && dep<=R) ans=max(ans,F[dep]); for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; if (vis[V] || V==fa) continue; get_dis(V,x,dep+1,val+e[i].val); sz[x]+=sz[V]; } } void solve(int x){ int cnt=0; for (int i=last[x]; i; i=e[i].next) { int V=e[i].to; if (vis[V]) continue; Dep[V]=0; get_maxdis(V,x,1,V); E[++cnt]=(node1){Dep[V],V,e[i].val}; } sort(E+1,E+cnt+1,cmp); for (int i=1; i<=E[cnt].Dep; i++) G[i]=-1e9; for (int i=1; i<=cnt; i++){ for (int j=1; j<=E[i].Dep; j++) F[j]=-1e9; get_dis(E[i].id,x,1,E[i].val); int head=1,tail=0,top=0; for (int j=E[i].Dep; j>=1; j--){ int LL=L-j,RR=R-j; while (top+1<LL && top+1<=E[i-1].Dep) top++; while (top+1>=LL && top+1<=RR && top+1<=E[i-1].Dep){ top++; while (head<=tail && G[top]>G[q[tail]]) tail--; q[++tail]=top; } while (head<=tail && q[head]<LL) head++; if (head<=tail) ans=max(ans,G[q[head]]+F[j]); if (head<=tail) ANS=max(ANS,(G[q[head]]+F[j]+cntkey*(j+q[head]))/(j+q[head])); } for (int j=1; j<=E[i].Dep; j++) G[j]=max(G[j],F[j]); } } void divide(int x){ vis[x]=1; solve(x); for (int i=last[x]; i; i=e[i].next){ int V=e[i].to; if (vis[V]) continue; root=0; N=sz[V]; find_root(V,x); divide(root); } } double check(double key){ for (int i=1; i<=n; i++) vis[i]=0; for (int i=1; i<=Cnt; i++) e[i].val-=key; ans=-1e9,ANS=-1; N=n; find_root(1,0); divide(root); for (int i=1; i<=Cnt; i++) e[i].val+=key; return ANS; } double check1(double key){ for (int i=1; i<=n; i++) vis[i]=0; for (int i=1; i<=Cnt; i++) e[i].val-=key; ans=-1e9,ANS=-1; N=n; find_root(1,0); divide(root); for (int i=1; i<=Cnt; i++) e[i].val+=key; return ans>=0; } int main(){ scanf("%d%d%d",&n,&L,&R); double Max=-1e9,Min=1e9; for (int i=1; i<n; i++){ int x,y; double z; scanf("%d%d%lf",&x,&y,&z); add(x,y,z); add(y,x,z); Max=max(Max,z); Min=min(Min,z); } F_[0]=1e9; N=n; if (n>1000){ cntkey=0; while (1){ double Sum=check(cntkey); if (abs(Sum-cntkey)<eps) break; else cntkey=Sum; } printf("%.3lf\n",cntkey); } else{ double L=Min,R=Max; while (R-L>eps){ double mid=(L+R)/2; if (check1(mid)) L=mid; else R=mid; } printf("%.3lf\n",L); } return 0; }