CF1988D The Omnipotent Monster Killer
Problem
怪物们在一棵有 $n$ 个顶点的树上,编号为 $i(1\le i\le n)$ 的怪物位于编号为 $i$ 的顶点上,攻击力为 $a_i$ 。你需要与怪物战斗 $10^{100}$ 个回合。在每个回合中,会依次发生以下两步:
- 所有活着的怪物攻击你。你的生命值会按照所有活体怪物攻击点的总和减少。
- 您选择一些(可以选全部,也可以不选)怪物并杀死它们。被杀死的怪物将不会再进行攻击。
限制条件:在一个回合内不能杀死相邻的两只怪物。
如果您以最佳选择方式攻击的怪物,那么在所有回合后,您的健康值减少的最小值是多少?
$1\le t\le 10^4,1\le n\le 3\cdot 10^5,1\le a_i \le 10^{12},\sum n\le 3\cdot 10^5$
Solution
这是一道再经典不过的树形DP了。太惭愧了。
每个节点的贡献可以表示为 $w_i\cdot a_i$ 的形式,其中 $w_i$ 表示怪物 $i$ 是第 $w_i$ 次被杀死的。可以证明 $w_i$ 不会超过 $\log_2(n)$ 。
![]()
图:Taibo
上图中,欲构造出 $w_i=x$ 的点,需要将该点连接上 $w=1,2,\dots,x-1$ 的节点。设构造出 $\max w_i=n$ 的树至少需要 $tot_n$ 个节点,则存在
即得 $tot_n=2^n$ 。也就是说对于一张 $n$ 个节点的图,其至多需要 $\log_2(n)$ 次选择就可以将所有怪物杀死。
下面开始dp。设 $dp_{x,k}$ 表示若第 $k$ 次杀死怪物 $x$ , $x$ 子树内的怪物至少会产生多少点伤害。
$dp_{x,k}$ 由两部分组成:
- 在第 $k$ 次杀死怪物 $x$ 之前,怪物 $x$ 会产生 $k\cdot a_x$ 点伤害。
- $x$ 的子树内的怪物(除了 $x$ 本身)产生的伤害。
其中 $u(x)$ 表示点 $x$ 的儿子节点。
最后答案为 $\min dp_{root,k}$
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| #define N 300010
int n; int head[N],nxt[N*2],ver[N*2],cnt; void insert(int x,int y) { nxt[++cnt]=head[x]; head[x]=cnt; ver[cnt]=y; }
LL a[N];
#define K 25 #define inf (1ll<<62) LL dp[N][K+5];
void dfs(int x,int f) { for(int i=1;i<=K;i++) { dp[x][i]=a[x]*i; } for(int i=head[x];i;i=nxt[i]) { int y=ver[i]; if(y==f) continue; dfs(y,x); for(int j=1;j<=K;j++) { LL mn=inf; for(int k=1;k<=K;k++) { if(j!=k) { mn=min(mn,dp[y][k]); } } dp[x][j]+=mn; } } }
int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout.precision(10); int t=1; cin>>t; while(t--) { cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<n;i++) { int x,y;cin>>x>>y; insert(x,y); insert(y,x); } dfs(1,0); LL ans=inf; for(int i=1;i<=K;i++) { ans=min(ans,dp[1][i]); } cout<<ans<<endl; for(int i=1;i<=cnt;i++) { head[i]=nxt[i]=ver[i]=0; } cnt=0; } return 0; }
|