码迷,mamicode.com
首页 > 其他好文 > 详细

【HEOI 2018】林克卡特树

时间:2018-04-10 10:52:38      阅读:313      评论:0      收藏:0      [点我收藏+]

标签:技术分享   关于   ret   二分   表示   close   set   消失   ==   

先说60分的.
思路题解上很清晰:

问题似乎等价于选K+1条点不相交的链哎!
F(x,k,0/1/2)表示考虑以x为根的子树,选了k条链,点x的度数为0/1/2的最优解.

我说一下比较坑的地方吧:
1.初始化要-Inf(反正我不加这个会wa)
2.注意转移的顺序
3.别忘了突然出现新的路径或者突然消失了一个路径的时侯加减1
4.一定要割k下
细节说多不多,说少不少,还得自己打.
说一下100分的.
60分的瓶颈在于k,那么如果对于k没有限制的话,那么我们的转移就会变成O(n)的(和60分的dp是差不多的).
如何去掉k的限制呢,我们考虑,我们的答案数组关于下标是凸包(想一下就会发现很显然啊).那么我们想到凸包就会想卡他,那么也就是我们设斜率去卡到k,那么我们有了斜率会发生什么呢.
我们的问题转化为了——设斜率为cost,那么就是求,这棵树选取若干条不相交路径,得到的贡献是路径边权和,代价是每条路径花费cost,求最终答案(最大化收益),及其路径条数.
这样我们每次二分出斜率之后会O(n)出解,得到路径条数,从而继续二分.
不难打,有了60分的dp之后,给到二分斜率的思路就应该可以写出来了.

技术分享图片
#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS,*xT;
#define gtc() (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){
    register char ch=gtc();
    bool ud=false;
    for(x=0;ch<0||ch>9;ch=gtc())
        if(ch==-)
            ud=true;
    for(;ch>=0&&ch<=9;ch=gtc())
        x=x*10+ch-0;
    if(ud)x=-x;
}
typedef long long LL;
const int N=300010;
const LL Inf=1e15;
struct V{
  int to,next,w;
}c[N<<1];
int head[N],t;
inline void add(int x,int y,int z){
  c[++t].to=y,c[t].next=head[x],head[x]=t,c[t].w=z;
}
int n;
LL k;
struct A{
  int x;LL y;
  inline void reset(){x=0,y=0;}
  inline void set(){x=0,y=-Inf;}
  inline A up(){
    A ret=*this;
    ++ret.x,ret.y-=k;
    return ret;
  }
  inline void cover(A a){
    if(a.y>y||(a.y==y&&a.x<x))
      (*this)=a;
  }
  inline void cover(A a,A b){
    if(a.y==-Inf||b.y==-Inf)return;
    a.x+=b.x,a.y+=b.y;
    cover(a);
  }
  inline void cover(A a,A b,LL w,int opt){
    if(a.y==-Inf||b.y==-Inf)return;
    a.x+=b.x-opt,a.y+=b.y+w+(opt?k:0);
    if(a.x<=0)return;
    cover(a);
  }
}f[N][3];
inline void dfs(int x,int fa){
  int i,v;
  f[x][0].reset();
  f[x][1].set();
  f[x][2].set();
  for(i=head[x];i;i=c[i].next){
    v=c[i].to;
    if(v==fa)continue;
    dfs(v,x);
    f[x][2].cover(f[x][2],f[v][2]);
    f[x][2].cover(f[x][1],f[v][1],c[i].w,1);
    f[x][1].cover(f[x][1],f[v][2]);
    f[x][1].cover(f[x][0],f[v][1],c[i].w,0);
    f[x][0].cover(f[x][0],f[v][2]);
  }
  f[x][1].cover(f[x][0].up());
  f[x][2].cover(f[x][1]);
  f[x][2].cover(f[x][0]);
}
inline A solve(){
  dfs(1,0);
  return f[1][2];
}
int main(){
  int need;
  read(n),read(need);
  ++need;
  int i,x,y,z;
  for(i=1;i<n;++i){
    read(x),read(y),read(z);
    add(x,y,z),add(y,x,z);
  }
  LL l=-1e12,r=1e12,mid,ans=0;
  A ret;
  while(l<=r){
    k=mid=l+r>>1;
    ret=solve();
    if(ret.x<=need)
      ans=ret.y+need*k,r=mid-1;
    else
      l=mid+1;
  }
  printf("%lld\n",ans);
  return 0;
}
Kod

 

【HEOI 2018】林克卡特树

标签:技术分享   关于   ret   二分   表示   close   set   消失   ==   

原文地址:https://www.cnblogs.com/TSHugh/p/8776179.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!