【HEOI 2018】林克卡特树
转载请注明出处:http://www.cnblogs.com/TSHugh/p/8776179.html
先说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之后,给到二分斜率的思路就应该可以写出来了.
(补充一下:这玩意似乎是wqs二分……)
#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; }
苟利国家生死以, 岂因祸福避趋之。