[BZOJ1758/WC2010]重建计划
Description
Input
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数\(A_i,B_i,V_i\)分别表示道路\((A_i,B_i)\),其价值为\(V_i\) 其中城市由1..N进行标号
Output
输出最大平均估值,保留三位小数
Sample Input
4
2 3
1 2 1
1 3 2
1 4 3
Sample Output
2.500
HINT
\(N\leqslant 100000,1\leqslant L\leqslant U\leqslant N-1,V_i\leqslant 1000000\)
考虑二分答案,然后把边权减去二分出的答案,判断是否存在>0的路径即可
点分治合并路径的时候记得按秩合并,即将子树按最深深度排序后枚举,开一个单调队列,统计完一棵子树后累计答案
二分有好几个位置,最外面、点分时、枚举子树时,放最外面常数最大(然而我就是这么写的),放在中间的位置可以随时改变二分上下界
为了卡常,可以先把点分治的重心枚举顺序先弄出来,然后类似for循环去点分治(因为luogu单点时限,Bzoj不需要)
/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
int x=0,f=1; char ch=gc();
for (;ch<'0'||ch>'9';ch=gc()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline int read(){
int x=0,f=1; char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
return x*f;
}
inline void print(int x){
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+'0');
}
const int N=1e5;
const double eps=1e-10,unit=1e-4;
int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],val[(N<<1)+10];
int size[N+10],Df[N+10],Root[N+10];
bool vis[N+10],flag;
double DV[N+10],Del;
int root,Max,r_sz;
int n,L,R,tot;
void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
void insert(int x,int y,int z){join(x,y,z),join(y,x,z);}
void Get_root(int x,int fa,int sz){
int res=0; size[x]=1;
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa||vis[son]) continue;
Get_root(son,x,sz);
size[x]+=size[son];
res=max(res,size[son]);
}
res=max(res,sz-size[x]);
if (res<Max) Max=res,root=x;
}
void get_Df(int x,int fa,int deep){
Df[x]=deep;
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa||vis[son]) continue;
get_Df(son,x,deep+1);
Df[x]=max(Df[x],Df[son]);
}
}
void get_dis(int x,int fa,double v,int Dp){
if (Dp>R) return;
DV[Dp]=max(DV[Dp],v);
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (son==fa||vis[son]) continue;
get_dis(son,x,v+val[p]-Del,Dp+1);
}
}
struct S1{
int x,Deep; double v;
oid insert(int _x,int _D,double _v){x=_x,Deep=_D,v=_v;}
bool operator <(const S1 &tis)const{return Deep<tis.Deep;}
}A[N+10];
double f[N+10];
void solve(double *a,int &la,double *b,int &lb){
la=min(la,R),lb=min(lb,R);
static int h[N+10]; int head=1,tail=0;
for (int i=lb,j=0;i;i--){
while (j<=la&&i+j<=R){
while (head<=tail&&a[h[tail]]<=a[j]) tail--;
h[++tail]=j++;
}
while (head<=tail&&h[head]+i<L) head++;
if (head>tail) continue;
if (a[h[head]]+b[i]>-eps){
flag=1;
return;
}
}
}
void merge(double *a,int &la,double *b,int &lb){for (int i=1;i<=max(la,lb);i++) a[i]=max(a[i],b[i]),b[i]=-inf; la=lb;}
void divide(int len){
if (len>n) return;
int x=Root[len];
if (flag) return;
is[x]=1; int son_sz=0;//num of son
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (vis[son]) continue;
get_Df(son,0,1),Df[son]=min(Df[son],R);
A[++son_sz].insert(son,Df[son],val[p]-Del);
}
int f_sz=0;
sort(A+1,A+1+son_sz);
if (A[son_sz].Deep<<1<L){
divide(len+1);
return;
}
for (int i=1;i<=son_sz;i++){
get_dis(A[i].x,0,A[i].v,1);
if (f_sz+A[i].Deep>=L){
solve(f,f_sz,DV,A[i].Deep);
if (flag) return;
}
merge(f,f_sz,DV,A[i].Deep);
}
if (flag) return;
for (int i=1;i<=f_sz;i++) f[i]=-inf;
divide(len+1);
}
bool check(double v){
Del=v,flag=0;
memset(vis,0,sizeof(vis));
for (int i=1;i<=R;i++) DV[i]=f[i]=-inf;
divide(1);
return flag;
}
void Frt(int x){//Find root
is[Root[++r_sz]=x]=1;
for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
if (vis[son]) continue;
root=0,Max=inf;
Get_root(son,0,size[son]);
Frt(root);
}
is[x]=0;
}
int main(){
n=read(),L=read(),R=read();
double l=1e9,r=0;
for (int i=1;i<n;i++){
int x=read(),y=read(),z=read();
insert(x,y,z);
l=min(l,1.0*z);
r=max(r,1.0*z);
}
root=0,Max=inf;
Get_root(1,0,n);
Frt(root);
while (l<=r){
double mid=(l+r)/2;
if (check(mid)) l=mid+unit;
else r=mid-unit;
}
printf("%.3lf\n",r);
return 0;
}