1. 程式人生 > >【HEOI 2018】林克卡特樹

【HEOI 2018】林克卡特樹

for 瓶頸 二分 close LG cli 就會 -c -i

先說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】林克卡特樹