1758: [Wc2010]重建计划
Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 989 Solved: 345
[ Submit][ Status]
Description
Input
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号
Output
输出最大平均估值,保留三位小数
Sample Input
4
2 3
1 2 1
1 3 2
1 4 3
2 3
1 2 1
1 3 2
1 4 3
Sample Output
2.500
HINT
20%的数据,N<=5000
30%的数据,N<=100000,原有方案恰好为一条路径
100%的数据,N<=100000,1<=L<=U<=N-1,Vi<=1000000
01分数规划+点分治。
做这道题的时候先看30%的数据,01分数规划+队列优化dp很好做。
那么对于一棵树呢?
用点分治来做就可以了!
01分数规划就是二分答案ans,然后把边权设为ans-原边权,若最小边权和小于0,那么增大ans,反之减小ans。
考虑经过根结点的(边权是更新过的)最小边权和:
枚举每一棵子树,用队列优化的dp来计算他与已经计算过的子树的满足>=l,<=u的最小边权和。
再递归处理每一个儿子。
于是整棵树的最小边权和就求出来了,若<=0,那么增大ans,反之减小ans。
直接这样写,TLE了5个点。
优化:
1.把二分写在点分治里面:
处理每一棵子树之前先二分求出经过当前树根的ans,在以后二分的时候把下界直接设成ans。
2.二分的时候根据优化1,可以感觉到最后的结果是偏向下界的,所以把mid=(l*19+r)/20
加上这些优化还是TLE了两个点TAT!!
求帮助。。
#include <iostream> #include <algorithm> #include <cstdio> #include <cstring> #include <cstdlib> #define eps 1e-4 #define ld long double #define M 100005 #define inf 0x3f3f3f3f using namespace std; bool f; int tt=0; int h[M],n,aa,aaa,tot,now,ti=0,s[M],l,u,ma[M],root,size,siz,done[M]; ld ans,Ans,a[M],b[M],c[M],mm=0.00; struct edge { int y,ne; ld v; }e[M*3]; struct Q { ld v; int p; }q[M]; void Addedge(int x,int y,ld v) { e[++tot].y=y; e[tot].v=v; e[tot].ne=h[x]; h[x]=tot; } void read(int &tmp) { tmp=0; int fu=1; char ch=getchar(); for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') fu=-1; for (;ch>='0'&&ch<='9';ch=getchar()) tmp=tmp*10+ch-'0'; tmp*=fu; } void Getroot(int x,int fa) { ma[x]=0,s[x]=1; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (y==fa||done[y]) continue; Getroot(y,x); s[x]+=s[y]; if (ma[x]<s[y]) ma[x]=s[y]; } if (ma[x]<siz-s[x]) ma[x]=siz-s[x]; if (ma[x]<ma[root]) root=x; } int Getsize(int x,int fa) { s[x]=1; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (done[y]||y==fa) continue; s[x]+=Getsize(y,x); } return s[x]; } void dfs(ld k,int x,int fa,int dep) { if (dep>u) return; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (done[y]||y==fa) continue; c[y]=c[x]+k-e[i].v; if (c[y]<b[dep]) b[dep]=c[y]; dfs(k,y,x,dep+1); } if (dep>aa) aa=dep; } void dp() { int ql=1,qr=0; for (int i=aaa;i>=l;i--) { while (ql<=qr&&a[i]<=q[qr].v) qr--; q[++qr].v=a[i],q[qr].p=i; } for (int i=0;i<=aa;i++) { if (q[ql].p==u-i+1) ql++; if (ql<=qr&&ans>q[ql].v+b[i]) ans=q[ql].v+b[i]; if (i>=l) continue; while (ql<=qr&&a[l-i-1]<=q[qr].v) qr--; q[++qr].v=a[l-i-1],q[qr].p=l-i-1; } } ld Solve(int x,ld k) { ans=inf; b[0]=0,a[0]=0; aaa=0,aa=0; for (int i=1;i<=u;i++) b[i]=a[i]=inf; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (done[y]) continue; b[1]=c[y]=k-e[i].v; aa=0; dfs(k,y,x,2); dp(); if (ans<=0.00) {f=true;return ans;} for (int j=1;j<=u;j++) if (b[j]<a[j]) a[j]=b[j],b[j]=inf; if (aa>aaa) aaa=aa; } return ans; } void Work(int x,int size) { if (size-1<l) { done[x]=1; return; } ld L=Ans,R=mm; while (R-L>eps) { ld mid=(L*19.0+R)/(20.0); if (Solve(x,mid)<=0) L=mid; else R=mid; } Ans=L; done[x]=1; for (int i=h[x];i;i=e[i].ne) { int y=e[i].y; if (done[y]) continue; siz=Getsize(y,0); root=0,ma[0]=inf; Getroot(y,0); Work(root,siz); } } int main() { read(n),read(l),read(u); for (int i=1;i<n;i++) { int x,y,v; read(x),read(y),read(v); if ((ld)v>mm) mm=v; Addedge(x,y,(ld)v),Addedge(y,x,(ld)v); } for (int i=1;i<=n;i++) done[i]=0; siz=n; root=0; ma[0]=inf; Getroot(1,0); Ans=0.00; Work(root,n); printf("%.3Lf\n",Ans); return 0; }