题目描述
传送门
题解
这道题一直在TLE,但是bzoj发过来的数据都可以在1s内出解,不是很懂为什么。
要最大化所选边的平均值,这是个01分数规划的问题,我们只需要二分答案然后判断树中是否有一条长度在[L,U]之间的链
∑s∈eval[s]−mid
大于0即可。
在点分治中01分数规划的效率要高于在外层01分数规划,因为这样不用每次check的时候都找重心。
还有如果一个点子树的
size<L
,那么就没必要计算了。
在统计一个点的答案的时候,注意并不是每次上届都是U,应该是min(U,size[i])这样可以减少很多不必要的运算。
因为合成的一条路径不能是出自一个点的同一棵子树,所以我们为何一个mx[i]数组表示的是深度为i的已经计算过的点的路径最大值,算当前子树的时候用bfs进行遍历,这样可以保证长度单调不降,那么在统计答案的时候就可以为何一个单调递减的单调栈,线性的更新答案。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 200003
#define eps 1e-5
using namespace std;
int n,point[N],son[N],nxt[N],v[N],tot,q[N],head,tail,size[N],f[N],root,ti,L,U,cnt,top,sz;
double len[N],mx[N],p[N],f1[N],v1,K,ans;
bool vis[N];
struct data{
int now,x,f; double y;
data(int No=0,int X=0,int F=0,double Y=0){
now=No,x=X,f=F,y=Y;
}
}a[N],st[N];
int cmp(data a,data b)
{
return a.x<b.x;
}
int get(){
char ch=getchar();int x=0;
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';ch=getchar();
}return x;
}
void add(int x,int y,double z)
{
tot++; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; len[tot]=z;
tot++; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; len[tot]=z;
}
void getroot(int x,int fa)
{
size[x]=1; f[x]=0;
for (int i=point[x];i;i=nxt[i]){
if (v[i]==fa||vis[v[i]]==1) continue;
getroot(v[i],x);
size[x]+=size[v[i]];
f[x]=max(f[x],size[v[i]]);
}
f[x]=max(f[x],tot-size[x]);
if (f[x]<f[root]) root=x;
}
void get_deep(int x,int fa,int dep,double l)
{
int s=0,t=0;
a[++t]=data(x,dep,fa,l);
while (s<t) {
++s;
int now=a[s].now; int f=a[s].f;
for (int i=point[now];i;i=nxt[i]){
if (vis[v[i]]==1||v[i]==f) continue;
if (a[s].x+1<=top) {
++t; a[t].x=a[s].x+1;
a[t].now=v[i]; a[t].f=now; a[t].y=a[s].y+len[i]-v1;
}
}
}
cnt=t;
}
bool calc(int x,double t)
{
v1=t; top=min(size[x],U); sz=0;
for (int i=1;i<=top;i++) mx[i]=-1e18;
for (int i=point[x];i;i=nxt[i]) {
if (vis[v[i]]==1) continue;
st[++sz].now=v[i]; st[sz].x=size[v[i]];
st[sz].y=len[i];
}
sort(st+1,st+sz+1,cmp);
for (int i=1;i<=sz;i++) {
cnt=0;
if (vis[st[i].now]==1) continue;
//cout<<st[i].now<<" ";
get_deep(st[i].now,x,1,st[i].y-v1);
//for (int j=cnt;j>=1;j--) cout<<a[j].now<<" "<<a[j].x<<" "<<a[j].y<<endl;
head=tail=1; q[1]=top; p[1]=mx[top];
for (int j=1;j<=cnt;j++) {
for (int k=q[tail]-1;k>=L-a[j].x;k--){
while (mx[k]>=p[tail]&&head<=tail) tail--;
++tail; q[tail]=k; p[tail]=mx[k];
}
while (q[head]>U-a[j].x&&head<=tail) head++;
if (head<=tail&&p[head]+a[j].y+eps>=0) return true;
}
for (int j=1;j<=cnt;j++) mx[a[j].x]=max(mx[a[j].x],a[j].y);
}
//cout<<endl;
return false;
}
void dfs(int x)
{
double l=ans; double r=K;
while (r-l>eps) {
double mid=(l*9.0+r)/10.0;
if (calc(x,mid)) l=mid;
else r=mid;
} ans=l; vis[x]=1;
for (int i=point[x];i;i=nxt[i]) {
if (vis[v[i]]==1) continue;
if (size[v[i]]<L) continue;
root=0; tot=size[v[i]];
getroot(v[i],x);
dfs(root);
}
}
int main()
{
freopen("rebuild.in","r",stdin);
freopen("rebuild.out","w",stdout);
scanf("%d",&n); scanf("%d%d",&L,&U);
K=0;
for (int i=1;i<n;i++) {
int x,y; double z;
x=get(); y=get(); z=(double)get();
add(x,y,z);K=max(K,z);
}
f[0]=1000000000; root=0; tot=n;
getroot(1,0); ans=0;
dfs(root);
printf("%.3lf\n",ans);
}