1. 程式人生 > >【JZOJ4923】【NOIP2017提高組模擬12.17】巧克力狂歡

【JZOJ4923】【NOIP2017提高組模擬12.17】巧克力狂歡

題目描述

Alice和Bob有一棵樹(無根、無向),在第i個點上有ai個巧克力。首先,兩人個選擇一個起點(不同的),獲得點上的巧克力;接著兩人輪流操作(Alice先),操作的定義是:在樹上找一個兩人都沒選過的點並獲得點上的巧克力,並且這個點要與自己上一次選的點相鄰。當有一人無法操作 時,另一個人可以繼續操作,直到不能操作為止。因為Alice和Bob是好朋友,所以他們希望兩人得到的巧克力總和儘量大,請輸出最大總和。

資料範圍

對於20%的資料,n<=15
對於40%的資料,n<=100
對於60%的資料,n<=5000
對於100%的資料,n<=200000,0<=ai<=1000000000(1e9)

=w=

題目模型顯然:尋找一棵樹上的兩條不相交的鏈使得權值和最大。
樹形動態規劃。
分兩種情況討論:
1.兩條鏈靠近(存在一條樹邊,兩個端點分居這兩條鏈中);
2.兩條鏈分居一個結點的兩顆子樹。
易推。

程式碼

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;
const char* fin="aP2.in";
const char* fout="aP2.out";
const ll inf=0x7fffffff;
const ll maxn=200007,maxm=maxn*2;
ll n,i,j,k,maxx,mxid,ans;
ll a[maxn],fi[maxn],la[maxm],ne[maxm],tot;
ll f[maxn],g[maxn],st[maxn],pre[maxn],spre[maxn],suf[maxn],ssuf[maxn];
ll Pre[maxn],Suf[maxn];
ll h[maxn],h1[maxn];
bool ban[maxn],ok[maxn];
void add_line(ll a,ll b){
    tot++;
    ne[tot]=fi[a];
    la[tot]=b;
    fi[a]=tot;
}
void xdfs(ll v,ll from){
    ll i,j,k,maxx,smaxx,tmp;
    st[0]=0;
    for (k=fi[v];k;k=ne[k]) if (la[k]!=from) st[++st[0]]=g[la[k]];
    if (from) st[++st[0]]=h1[v];
    maxx=smaxx=0;
    for (i=1;i<=st[0];i++){
if (maxx<st[i]){ smaxx=maxx; maxx=st[i]; }else smaxx=max(st[i],smaxx); pre[i]=maxx; spre[i]=smaxx; } suf[st[0]+1]=ssuf[st[0]+1]=0; maxx=smaxx=0; for (i=st[0];i;i--){ if (maxx<st[i]){ smaxx=maxx; maxx
=st[i]; }else smaxx=max(st[i],smaxx); suf[i]=maxx; ssuf[i]=smaxx; } tmp=0; for (k=fi[v];k;k=ne[k]) if (la[k]!=from) { tmp++; i=max(pre[tmp-1],suf[tmp+1]); j=max(min(pre[tmp-1],suf[tmp+1]),max(spre[tmp-1],ssuf[tmp+1])); h1[la[k]]=i+a[v]; h[la[k]]=a[v]+i+j; } for (k=fi[v];k;k=ne[k]) if (la[k]!=from) xdfs(la[k],v); ans=max(ans,h[v]+f[v]); } void dfs(ll v,ll from){ ll i,j,k,maxx,smaxx,tmp; g[v]=a[v]; for (k=fi[v];k;k=ne[k]){ if (la[k]!=from){ dfs(la[k],v); g[v]=max(g[v],g[la[k]]+a[v]); f[v]=max(f[v],f[la[k]]); } } st[0]=0; for (k=fi[v];k;k=ne[k]) if (la[k]!=from) st[++st[0]]=la[k]; maxx=smaxx=0; for (i=1;i<=st[0];i++){ if (maxx<g[st[i]]){ smaxx=maxx; maxx=g[st[i]]; }else smaxx=max(g[st[i]],smaxx); pre[i]=maxx; spre[i]=smaxx; Pre[i]=max(Pre[i-1],f[st[i]]); } Suf[st[0]+1]=suf[st[0]+1]=ssuf[st[0]+1]=0; maxx=smaxx=0; for (i=st[0];i;i--){ if (maxx<g[st[i]]){ smaxx=maxx; maxx=g[st[i]]; }else smaxx=max(g[st[i]],smaxx); suf[i]=maxx; ssuf[i]=smaxx; Suf[i]=max(Suf[i+1],f[st[i]]); } f[v]=max(f[v],maxx+smaxx+a[v]); tmp=0; for (k=fi[v];k;k=ne[k]) if (la[k]!=from){ tmp++; i=max(pre[tmp-1],suf[tmp+1]); j=max(min(pre[tmp-1],suf[tmp+1]),max(spre[tmp-1],ssuf[tmp+1])); ans=max(ans,f[la[k]]+i+j+a[v]); ans=max(ans,f[la[k]]+max(Pre[tmp-1],Suf[tmp+1])); } } int main(){ scanf("%lld",&n); for (i=1;i<=n;i++) scanf("%lld",&a[i]); for (i=1;i<n;i++){ scanf("%lld%lld",&j,&k); add_line(j,k); add_line(k,j); } dfs(1,0); xdfs(1,0); printf("%lld",ans); return 0; }

=o=

求最大值和次大值,可以維護三大邊。
不用像我維護什麼字首最大值,和字尾最大值= =