分析
一眼 DP。
定義狀態函式 $f_{i,j}$ 表示在第 $i$ 此比賽中,獲勝者為 $j$ 時的最大獎學金。把比賽過程看成一棵倒著的滿二叉樹,就能發現:第 $i$ 場比賽只會是其左兒子為根的子樹中葉子節點的某一個與其右兒子為根的子樹中葉子節點的某一個進行比賽。然後就可以得到轉移方程:$f_{i,j}=f_{lst,j}-c_{j,dep_{lst}}+c_{j,dep_i}+\max{f_{lst',k}}$。其中 $lst$ 表示 $j$ 上一次獲勝的場次,$dep_i$ 表示從葉子節點到 $i$ 的距離,$k$ 為與 $lst$ 相對的子樹中的某一個葉子節點,$lst'$ 就是 $i$ 的另一個兒子。
可以預處理出來 $dep_i,lst,lst'$。答案就是 $\max{f_{1,i}|1 \le i \le 2^{n}}$。這裡令根節點編號為 $1$。
程式碼
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define re register
#define il inline
const int N=2e5+10,M=20;
int n,c[N][M];
map<int,int> f[N];
int lson[N],rson[N];
vector<int> ve[N];
int dep[N];
il void solve(){
cin>>n;
for(re int i=1;i<=pow(2,n);++i)
for(re int j=1;j<=n;++j)
cin>>c[i][j];
for(re int i=pow(2,n+1)-1;i>1;--i){
if(i&1) lson[i/2]=i;
else rson[i/2]=i;
}
int nowdep=1,lst=pow(2,n-1);
for(re int i=pow(2,n)-1;i>=1;--i){
if(!lst) ++nowdep,lst=pow(2,n-nowdep);
dep[i]=nowdep;
--lst;
}
for(re int i=pow(2,n+1)-1,k=1;i>=pow(2,n+1)-1-pow(2,n)+1;--i,++k) ve[i].push_back(k);
for(re int i=pow(2,n)-1;i>=1;--i){
int maxlson=0,maxrson=0;
for(re int j=0;j<ve[lson[i]].size();++j){
int x=ve[lson[i]][j];ve[i].push_back(x);
maxlson=max(maxlson,f[lson[i]][x]);
}
for(re int j=0;j<ve[rson[i]].size();++j){
int x=ve[rson[i]][j];ve[i].push_back(x);
maxrson=max(maxrson,f[rson[i]][x]);
}
for(re int j=0;j<ve[lson[i]].size();++j){
int x=ve[lson[i]][j];
f[i][x]=max(f[i][x],f[lson[i]][x]-c[x][dep[lson[i]]]+c[x][dep[i]]+maxrson);
}
for(re int j=0;j<ve[rson[i]].size();++j){
int x=ve[rson[i]][j];
f[i][x]=max(f[i][x],f[rson[i]][x]-c[x][dep[rson[i]]]+c[x][dep[i]]+maxlson);
}
}
int maxx=0;
for(re int i=1;i<=pow(2,n);++i) maxx=max(maxx,f[1][i]);
cout<<maxx;
return ;
}
signed main(){
solve();
return 0;
}