LuoguP6748 『MdOI R3』Fallen Lord 樹形DP+set

首先,肯定沒有不合法情況(每條邊的權值都賦值爲 $1$ 就一定合法)   

然後對於一條邊 $(x,y)$ 來說,只可能有 3 種取值.  

1. 取 $a[x]$ 

2. 取 $a[y]$ 

3. 取 $m$    

然後轉化成這一步後就可以進行樹形 DP 了.  

令 $f[x][0],f[x][1]$ 分別表示以 $x$ 爲根的子樹,且 $x$ 與 $x$ 父親連邊的邊權小於等於 $a[x]$/大於 $a[x]$ 的最大權和.     

這個裸做的話是一個 $O(n^2)$ 的樹形揹包.  

但是我們發現這個問題中所有物品的體積都是 $1$,那麼我們就可以先貪心選取一個兒子 $y$ 的最優決策點.   

如果該決策點沒有讓邊權小於等於 $a[x]$ ,就把差量扔進一個 set 裏,貪心取出需要補齊的部分就行了.  

代碼: 

#include <cstdio>
#include <set>
#include <vector>
#include <cstring>
#include <algorithm> 
#define N 500009  
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;  
const ll inf=1000000000;    
int n,m,edges;  
ll f[N][2];  
int deg[N]; 
int a[N],hd[N],to[N<<1],nex[N<<1];  
void add(int u,int v) { 
    nex[++edges]=hd[u]; 
    hd[u]=edges,to[edges]=v; 
}
multiset<ll>se[N];          
multiset<ll>::iterator it;  
void dfs(int x,int ff) {  
    int det=deg[x]/2+1,cnt=0;      
    // f[x][0] : det-1  
    // f[x][1] : det     
    f[x][0]=f[x][1]=0;     
    for(int i=hd[x];i;i=nex[i]) { 
        int y=to[i];   
        if(y==ff) continue;  
        dfs(y,x);     
        ll cur=max(f[y][0]+a[y],f[y][1]+m);       
        f[x][0]+=cur;  
        f[x][1]+=cur;   
        if(a[x]>=a[y]) { 
            if(f[y][1]+m==cur&&m>a[x]) {             
                se[x].insert(-(max(f[y][0]+a[y],f[y][1]+a[x])-cur));           
            }  
            else ++cnt;    
        } 
        else {
            se[x].insert(-(max(f[y][0]+a[x],f[y][1]+a[x])-cur));    
        }
    }    
    int k0=det-1,k1=det;      
    if(cnt+se[x].size()>=k0) {    
        it=se[x].begin();   
        for(int i=1;i<=k0-cnt;++i)   {  
            f[x][0]-=(*it);              
            it++;   
        }
    }       
    else f[x][0]=-inf;   
    if(cnt+se[x].size()>=k1) {  
        it=se[x].begin();  
        for(int i=1;i<=k1-cnt;++i)   {   
            f[x][1]-=(*it); 
            it++;   
        }
    }  
    else f[x][1]=-inf; 
    se[x].clear();   
    if(deg[x]==1&&ff) {   
        f[x][0]=0;   
        f[x][1]=-inf;   
    }   
}   
int main() {
    // setIO("input");   
    scanf("%d%d",&n,&m);   
    for(int i=1;i<=n;++i) { 
        scanf("%d",&a[i]); 
        a[i]=min(a[i],m);    
    }   
    int x,y,z; 
    for(int i=1;i<n;++i) { 
        scanf("%d%d",&x,&y); 
        add(x,y);  
        add(y,x);   
        ++deg[x]; 
        ++deg[y];  
    }         
    dfs(1,0);   
    printf("%lld\n",f[1][1]);      
    return 0; 
}

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章