zoukankan      html  css  js  c++  java
  • 【2019北京集训六】路径(path) 二分+DP

    此题niubi!

    题目大意:给你一颗n个点的点带权无根树,现在请您进行以下两步操作:

    1,选择一个$[0,T]$之间的整数$C$,并令所有的点权$wi$变为$(wi+C)%MOD$

    2,选择若干条点不相交的路径;设选择的条数为$k$,覆盖的点的点权和为$S$,则收益为$frac{S}{k+1}$

    请您求出收益最大可能为多少。

    数据范围:$T,S≤10^5$,$n≤5000$

    我们先不考虑修改点权的过程,只考虑求最大收益应该如何做。

    我们显然有一种$O(n^2)$的做法,但是复杂度太高了,加上修改点权的操作就爆炸了,考虑优化。

    我们考虑二分这个收益,设当前二分到的收益是$ans$。

    假设真实收益大于$ans$,则有:

    $frac{S}{k+1}>ans$

    我们进行移项,有:

    $S-k imes ans>ans$

    这个式子为啥会优秀很多呢?因为$S-k imes ans$可以在$O(n)$的时间内求出了:

    我们设$f[u]$表示以$u$为跟的子树内找出了$x$条路径,($x$条路径的和$-x imes ans$)的最大值,且没有路径连出$u$

    $g[u]$表示以$u$为跟的子树内找出了$x$条路径,($x$条路径的和$-(x-1) imes ans$)的最大值,有恰好一条路径从$u$连出

    我们在以$u$为跟的子树中找出两个儿子$v1$,$v2$,满足$v1$在所有儿子中,$g[v1]-f[v1]$最大,$v2$次大。

    设$S=sum limits_{v∈son[u]} f[u]$,则有:

    $f[u]=max(S,S+(g[v1]-f[v1])+(g[v2]-f[v2]+val[u])-ans)$

    $g[u]=max(S,S+(g[v1]-f[v1])+val[u])$

    其中$val[u]$表示点$u$的权值。

    我们通过二分$ans$,就可以在$O(nlog(-varepsilon))$确定当前最大的$ans$。

     

    我们考虑点权的修改,一个有效的点权修改方案,需满足至少一个$val[i]$被修改至$MOD-1$,或者增加的值为$T$。

    如果每次暴力修改,然后二分查找$ans$,这个复杂度显然是$O(nlog(-varepsilon))$的,依然会爆炸。

    设$Ans[i]$表示当点权被增加了$c[i]$时的$ans$

    我们考虑到,一个长度为$n$的随机序列的最长上升子序列期望长度是$O(log  n)$的

    我们考虑将序列$C$随机打乱

    我们先将序列进行修改,然后基于之前求出的$ans$,求一遍答案,判断修改后的序列是否会更加优秀。

    如果更加优秀,我们就重新二分出答案。

    不难发现,重新二分答案的次数期望是$O(log n)$的。

    总的复杂度就是$O(n^2+nlog nlog(-varepsilon))$的。

    完结撒花

     1 #include<bits/stdc++.h>
     2 #define M 5005
     3 #define eps 1e-6
     4 using namespace std;
     5 
     6 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0;
     7 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
     8 
     9 double f[M]={0},g[M]={0},Ans=0;
    10 double get(int x){return g[x]-f[x];}
    11 
    12 int n,MOD,T,val[M]={0},Val[M]={0};
    13 
    14 void dfs(int x,int fa){
    15     int max1=0,max2=0;
    16     double sumf=0;
    17     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
    18         int v=e[i].u;
    19         dfs(v,x);
    20         if(get(v)>get(max1)) max2=max1,max1=v;
    21         else if(get(v)>get(max2)) max2=v;
    22         sumf+=f[v];
    23     }
    24     f[x]=max(sumf,sumf+get(max1)+get(max2)+val[x]-Ans);
    25     g[x]=max(max(sumf,sumf+val[x]),sumf+get(max1)+val[x]);
    26 }
    27 
    28 int cnt=0,c[M]={0};
    29 
    30 bool check(double chg){
    31     Ans=chg;
    32     dfs(1,0);
    33     return f[1]>=chg-eps;
    34 }
    35 
    36 int main(){
    37     scanf("%d%d",&n,&MOD);
    38     for(int i=1;i<=n;i++) scanf("%d",Val+i),Val[i]%=MOD;
    39     for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    40     scanf("%d",&T);
    41     c[++cnt]=0; c[++cnt]=T;
    42     for(int i=1;i<=n;i++){
    43         c[++cnt]=MOD-1-Val[i];
    44         if(c[cnt]>T){c[cnt--]=0; continue;}
    45     }
    46     memcpy(val,Val,sizeof(val));
    47 
    48     random_shuffle(c+1,c+cnt+1);
    49     double ans=0;
    50     for(int hh=1;hh<=cnt;hh++){
    51         for(int i=1;i<=n;i++) val[i]=(Val[i]+c[hh])%MOD;
    52         if(!check(ans)) continue;
    53         double l=0,r=n*MOD/2;
    54         while(r-l>eps){
    55             double mid=(l+r)/2;
    56             if(check(mid)) l=mid;
    57             else r=mid;
    58         }
    59         ans=max(ans,l);
    60     }
    61     printf("%.10lf
    ",ans);
    62 }
  • 相关阅读:
    ubuntu Server 16.04 LTS 安装odoo
    linux常用命令大全
    sql 百万级数据库优化方案
    FreeSpire.XLS的使用
    备份集中的数据库与现有的数据库不同解决方案
    图片延迟加载的实现
    亚马逊菜单应用例子
    提取吗
    linux内核学习网站
    phpexcel1
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10677212.html
Copyright © 2011-2022 走看看