1758: [Wc2010]重建计划
Time Limit: 40 Sec Memory Limit: 162 MB
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
Sample Output
2.500
HINT
N<=100000,1<=L<=U<=N-1,Vi<=1000000
题解
这道题的要求是平均值最大,于是就想到了分数规划
分数规划详见大佬博客:https://www.cnblogs.com/captain1/p/9929128.html
感觉有点像带权二分(雾)
分数规划就是直接二分一下答案,然后把分母乘过来,移一下项,就会发现每一项都会减去一个对应的值
我们只需要把每一个点都赋上一个对应的值来进行验证答案就好啦
至于这道题,把|S|乘过来,再移项过去,就可以发现每一条边只需要赋为(长度-1*二分的答案),就可以求它们的最大值
来判断这个最大值是否为0
剩下的就是长链剖分+线段树的板子了
有一个小小的疑问,既然我们可以直接求在[L,U]限制下的最大值,为什么不直接求答案呢?
其实我们现在求的最大值只是保证了分子最大,而分母的值是不确定的,所以还是得二分答案。。。
还有一种比二分快10倍的方法,叫做迭代,详见大佬博客:https://blog.csdn.net/C20181220_xiang_m_y/article/details/102211422
下面具体讲一下长链剖分
其实长链剖分就是把自己的重儿子设置为拥有最深叶子节点的儿子。(类比重链剖分的重儿子设置的是子树节点最多的儿子)
然后类似于重链剖分
把整棵树的优先重儿子的dfs序求出来
这样一条链上的点就可以在线段树上对应一段连续的区间
如何求在[L,U]限制下的边权和最大值?
我们先存一个val[i],表示i节点到根的边权和
类似于dsu on tree的思路
先计算重儿子对答案的贡献(注意判断是否出界)
然后一个个计算轻儿子对答案的贡献
这时,轻儿子已经完成自己子树下面的计算,轻儿子的所有答案都已经合并到了轻儿子的长链上
所以就只用合并当前长链与轻儿子长链的maxval值就好了
总共O(nlogn),加上二分O(nlognlogn)
人生第一道长链剖分
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
char c;int num=0;
while((c=getchar())<'0'||c>'9');
do{num=num*10+c-48;c=getchar();}while(c>='0'&&c<='9');
return num;
}
int n,L,R;
#define LL long long
const LL INF=0x3f3f3f3f3f3f3f3fll;
const int EXP=10000;
#define N 100005
int fir[N],to[2*N],nxt[2*N],cnt;
LL cd[2*N];
void adde(int a,int b,LL c)
{
to[++cnt]=b;cd[cnt]=c;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;cd[cnt]=c;nxt[cnt]=fir[b];fir[b]=cnt;
}
int dep[N],hei[N],son[N],fa[N];
LL val[N];
void dfs1(int u)
{
hei[u]=dep[u]=dep[fa[u]]+1;
int v,p;
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=fa[u]){
fa[v]=u;val[v]=val[u]+cd[p];
dfs1(v);
hei[u]=max(hei[u],hei[v]);
if(hei[son[u]]<hei[v])
son[u]=v;
}
}
}
int pos[N],ind[N],dfn;
void dfs2(int u)
{
pos[u]=(++dfn);ind[dfn]=u;
if(son[u]) dfs2(son[u]);
int v,p;
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=fa[u]&&v!=son[u])
dfs2(v);
}
}
#define lc i<<1
#define rc i<<1|1
struct node{
int l,r;
LL mx;
}a[N<<2];
void build(int i,int l,int r)
{
a[i].l=l;a[i].r=r;a[i].mx=-INF;
if(l==r){a[i].mx=val[ind[l]];return;}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
a[i].mx=max(a[lc].mx,a[rc].mx);
}
void insert(int i,int x,LL k)
{
if(x<a[i].l||x>a[i].r) return;
if(a[i].l==x&&x==a[i].r){
a[i].mx=max(a[i].mx,k);
return;
}
insert(lc,x,k);
insert(rc,x,k);
a[i].mx=max(a[lc].mx,a[rc].mx);
}
LL query(int i,int l,int r)
{
if(r<a[i].l||a[i].r<l) return -INF;
if(l<=a[i].l&&a[i].r<=r)
return a[i].mx;
return max(query(lc,l,r),query(rc,l,r));
}
LL ans;
void solve(int u)
{
if(!son[u]) return;
int l=pos[u],r=pos[u]+hei[u]-dep[u],v,p,_L,_R,d,s;
solve(son[u]);
if(l+L<=r)
ans=max(ans,query(1,l+L,min(l+R,r))-val[u]);
for(p=fir[u];p;p=nxt[p]){
v=to[p];
if(v!=fa[u]&&v!=son[u]){
solve(v);
d=v;
while(d){
_L=L-(dep[d]-dep[v])-1,_R=R-(dep[d]-dep[v])-1;
if(l+_L<=r)
ans=max(ans,query(1,max(l+_L,l),min(l+_R,r))+val[d]-2*val[u]);
d=son[d];
}
d=v;s=son[u];
while(d){
insert(1,pos[s],val[d]);
val[s]=max(val[s],val[d]);
d=son[d];s=son[s];
}
}
}
}
bool check(LL mid)
{
for(int i=1;i<=cnt;i++)
cd[i]-=mid;
dfn=0;dfs1(1);dfs2(1);build(1,1,n);
ans=-INF;solve(1);
for(int i=1;i<=cnt;i++)
cd[i]+=mid;
return ans>=0;
}
int main()
{
int i,u,v,w;
LL l,r=0,mid;
n=gi();L=gi();R=gi();
for(i=1;i<n;i++){
u=gi();v=gi();w=gi();
adde(u,v,1ll*w*EXP);
r=max(r,1ll*w);
}
l=0;r=1ll*r*EXP;mid=(l+r)>>1;
while(l<r-3){
if(check(mid))
l=mid;
else
r=mid-1;
mid=(l+r)>>1;
}
for(mid=r;mid>=l;mid--){
if(check(mid)){
printf("%.3f",1.0*mid/EXP);
return 0;
}
}
}