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;
}

浙公网安备 33010602011771号