【HEOI 2018】Day2 T2 林克卡特樹


題目大意:

  給一個n個節點的樹,然后將其分成k+1個聯通塊,再在每個聯通塊取一條路徑,將其連接起來,求連接起來的路徑最大權值。

題解:

  考場只會20分,還都打掛了……

  60分的做法其實並不難,nk DP即可,設$f(i,j,0/1/2)$表示i子樹選取了j個聯通塊,i這個節點連了0/1/2條邊時的最優解。

  100分的做法就是60分做法的拓展。

  很容易想到一件事,就是以聯通塊數為x軸,最優解為y軸,那么這個圖像應該是一個單峰上凸函數。同時該離散函數每相鄰兩點間的斜率是遞減的:因為考慮當前聯通塊數為a,則當聯通塊數為a+1時,必然是在a時最優解上再連接一段新切出的可空路徑並割去一部分可空路徑,當補上一條新路徑時,我們很容易知道這次補上的路徑-割去路徑一定小於以前做的同樣操作(最優性)。

  這樣我們發現斜率是具有單調性的(即單調減)。那么我們二分這個斜率,並將原圖像減去這個斜率對應的正比例函數,會發現,新圖像將會在這個斜率對應點的位置最高,同時也是一個斜率遞減函數。

  那么我們考慮如何求出此時的答案:新圖像上的最高點權值+新圖像上最高點聯通塊數*斜率。

  我們考慮這個東西怎么求。

  設二元組$f(i,0/1/2)$表示i節點連了0/1/2條邊時的最優解和其聯通塊數(盡量小)。特別的沒有連邊的i,算為一個聯通塊,2為0/1/2這三個狀態的最優解。

  考慮這個東西怎么轉移:

  假設已經得到子節點v的答案。

  對於$f(x,2)$,我們有三種選擇,1.保持原來不變,把$f(v,2)$加上;2.由$f(x,1)$和$f(v,1)$合並;3.由$f(x,1)和f(v,0)$合並。

  $f(x,1)$,我們同樣有三種選擇,大體同上者。

  $f(x,0)$,我們只有一種選擇,即和$f(v,2)$結合。

  我們以$f(x,2)$為例:對於第一種情況,聯通塊數不變直接合並即可,對於第二種情況聯通塊數減少1,第三種情況同樣減少了1個聯通塊。

  得到結果以后比較k+1與最優解對應的聯通塊數,大於則說明斜率過小,否則說明斜率還可能更大。

代碼:

 1 #include "bits/stdc++.h"
 2 
 3 using namespace std;
 4 
 5 inline int read(){
 6     int s=0,k=1;char ch=getchar();
 7     while (ch<'0'|ch>'9') ch=='-'?k=-1:0,ch=getchar();
 8     while (ch>47&ch<='9') s=s*10+(ch^48),ch=getchar();
 9     return s*k;
10 }
11 
12 typedef long long ll;
13 
14 const int N=3e5+10;
15 
16 struct edges{
17     int v,w;edges *last;
18 }edge[N<<1],*head[N];int cnt;
19 
20 inline void push(int u,int v,int w) {
21     edge[++cnt]=(edges){v,w,head[u]},head[u]=edge+cnt;
22 }
23 
24 int n,k;
25 ll slope;
26 const ll inf=1e15;
27 
28 struct node {
29     ll val,num;
30     node(){val=num=0;}
31     node(ll v,ll nm):val(v),num(nm){}
32     inline ll &operator [](int x){
33         return x?num:val;
34     }
35     inline void max(node a){
36         if(a[0]>val||(a[0]==val&&a[1]<num))
37             (*this)=a;
38     }
39     inline void add(node a,node b){
40         if (a[0]==-inf||a[0]==-inf)    return ;
41         a[0]+=b[0],a[1]+=b[1];
42         max(a);
43     }
44     inline void add(node a,node b,int w,int opt){
45         if(a[0]==-inf||b[0]==-inf) return ;
46         a[1]+=b[1]-opt,a[0]+=b[0]+w+slope*opt;
47         if(a[1]<=0) return ;
48         max(a);
49     }
50     inline node fa(){
51         return  node(val-slope,num+1);
52     }
53 }f[N][3];
54 
55 inline void dp(int x,int fa){
56     f[x][0]=f[x][1]=f[x][2]=node();
57     f[x][1][0]=f[x][2][0]=-inf;
58     for (edges *i=head[x];i;i=i->last)    if(i->v!=fa) {
59         dp(i->v,x);
60         f[x][2].add(f[x][2],f[i->v][2]);
61         f[x][2].add(f[x][1],f[i->v][1],i->w,1);
62         f[x][2].add(f[x][1],f[i->v][0],i->w,0);
63         f[x][1].add(f[x][1],f[i->v][2]);
64         f[x][1].add(f[x][0],f[i->v][1],i->w,0);
65         f[x][1].add(f[x][0],f[i->v][0],i->w,-1);
66         f[x][0].add(f[x][0],f[i->v][2]);
67     }
68     f[x][2].max(f[x][1]);
69     f[x][2].max(f[x][0]);
70     f[x][2].max(f[x][0].fa());
71 }
72 
73 
74 int main(){
75     n=read(),k=read()+1;
76     for (int i=1;i<n;++i) {
77         int a=read(),b=read(),w=read();
78         push(a,b,w),push(b,a,w);
79     }
80     ll l=-1e12,r=1e12;
81     node now;
82     ll ans=0;
83     while (l<=r) {
84         slope=l+r>>1;
85         dp(1,0);
86         now=f[1][2];
87         if(now[1]<=k) 
88             ans=now[0]+slope*k,r=slope-1;
89         else l=slope+1;
90     }
91     printf("%lld\n",ans);
92 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM