CF2133D 鸡骑士
CF2133D 鸡骑士
洛谷题目传送门
题目描述
Steve 做出了夜间挖矿的愚蠢决定,并遭遇了一个可怕的生物:鸡骑士n^nn!
一个鸡骑士n^nn 由 nnn 个生物按顺序堆叠而成,最底层是生物 111,最顶层是生物 nnn。生物 iii 初始拥有 hih_ihi 点生命值。
每次攻击中,Steve 可以对任意一个生物造成 111 点伤害。当任意生物的生命值降至 000 或以下时,它会死亡,其上方所有生物会下落并形成一个新的堆叠。新堆叠最底层的生物会受到等同于先前堆叠中其下方生物数量(包括刚死亡的生物)的坠落伤害(即每压着一个生物就受到 111 点伤害)。这也可能导致该生物死亡,此时其上方所有生物会再次下落,并重复此过程。
例如,考虑一个初始生命值为 [1,2,1,3,5,2][1, 2, 1, 3, 5, 2][1,2,1,3,5,2] 的鸡骑士6^66。若 Steve 攻击堆叠中的第三个生物,它会死亡,而后生命值为 [3,5,2][3, 5, 2][3,5,2] 的生物会下落形成新堆叠。最底层的生物受到 333 点坠落伤害后死亡,随后生命值为 [5,2][5, 2][5,2] 的生物继续下落形成新堆叠。此时最底层的生物受到 111 点坠落伤害。最终,在 Steve 的第一次攻击后,将存在两个堆叠,生命值分别为 [1,2][1, 2][1,2] 和 [4,2][4, 2][4,2]。
Steve 的剑耐久度较低,因此他希望知道消灭所有生物所需的最少攻击次数。
输入格式
每个测试包含多个测试用例。第一行包含测试用例数量 ttt(1≤t≤1041 \le t \le 10^41≤t≤104)。接下来是各测试用例的描述。
每个测试用例的第一行包含一个整数 nnn(2≤n≤2⋅1052 \le n \le 2 \cdot 10^52≤n≤2⋅105)—— 生物数量。
每个测试用例的第二行包含 nnn 个整数 h1,h2,…,hnh_1, h_2,\ldots, h_nh1,h2,…,hn(1≤hi≤1091 \le h_i \le 10^91≤hi≤109)—— 每个生物的初始生命值。
保证所有测试用例的 nnn 之和不超过 2⋅1052 \cdot 10^52⋅105。
输出格式
对于每个测试用例,输出一个整数——消灭所有生物所需的最少攻击次数。
输入输出样例 #1
输入 #1
5
5
3 1 4 1 2
4
1 1 1 1
6
1 2 1 3 5 2
6
3 1 1 3 2 1
3
1000000000 1000000000 1000000000
输出 #1
7
1
7
5
2999999998
说明/提示
第一个测试用例中,初始堆叠生物生命值为 [3,1,4,1,2][3, 1, 4, 1, 2][3,1,4,1,2]。Steve 可通过一次攻击击杀第二个生物。第三个生物受到 222 点坠落伤害后,形成两个堆叠:[3][3][3] 和 [2,1,2][2, 1, 2][2,1,2]。随后 Steve 击杀第二个堆叠中的第二个生物,该堆叠的第三个生物受到 222 点坠落伤害而死亡。此时剩余两个堆叠:[3][3][3] 和 [2][2][2]。最终 Steve 通过五次攻击消灭剩余生物。
第二个测试用例中,Steve 对最底层生物造成 111 点伤害。当其死亡时,第二个生物会受到 111 点坠落伤害并死亡;接着第三个生物受到 111 点坠落伤害死亡;最终第四个生物受到 111 点坠落伤害死亡。
思路详解
题目分析
根据观察,我们发现同一时刻最多会出现2堆怪物,换句话说,同一时刻出现2对以上怪物不是最优的。这很显然,如果你先将一堆怪物[x][x][x]分裂成为[x,y][x,y][x,y],在将[y][y][y]分裂成[y,z][y,z][y,z],那你还不如先将[x][x][x]分裂为[x,z][x,z][x,z],再将其分裂为[x,y][x,y][x,y]。这有什么用呢?这说明的我们砍掉下面的怪物不会对上面的怪物的掉了高度产生影响,即我们的决策无后效性,那我们可以考虑dpdpdp了。
dp详解
dpdpdp的内容如下:
- dpdpdp定义:我们发现砍死下面的的怪会使上面的怪受到掉落伤害,所以我们定义dpidp_{i}dpi为消灭i−ni-ni−n的怪物需要的刀数。发现不好转移,考虑添加以为记录当前是否砍死使得上面的怪物掉下来,即dpi,1/0dp_{i,1/0}dpi,1/0。
- 状态转移:首先考虑dpi,0dp_{i,0}dpi,0,当前不砍死则上一位可砍可不砍,在加上最后要把他砍掉的次数aia_{i}ai,但是这对吗?这不对,我们发现只要当前这个不砍死使上面掉下来,则除非i==1i==1i==1,他都会受到1的坠落伤害。所以转移为dpi,0=min(dpi+1,0,dpi+1,1)+ai−(i!=1)dp_{i,0}=min(dp_{i+1,0},dp_{i+1,1})+a_{i}-(i!=1)dpi,0=min(dpi+1,0,dpi+1,1)+ai−(i!=1)。 再考虑dpi,1dp_{i,1}dpi,1,砍掉当前会对i+1i+1i+1产生iii的坠落伤害,后面的伤害已经记录,可以不管,所以转移为dpi,1=min(dpi+2,1,dpi+2,0)+(ai+1−i)+aidp_{i,1}=min(dp_{i+2,1},dp_{i+2,0})+(a_{i+1}-i)+a_{i}dpi,1=min(dpi+2,1,dpi+2,0)+(ai+1−i)+ai。
- dpdpdp边界:显然可得dpi+1,1/0=dpi+2,1/0=0dp_{i+1,1/0}=dp_{i+2,1/0}=0dpi+1,1/0=dpi+2,1/0=0
- 答案:答案即为min(dp1,1/0)min(dp_{1,1/0})min(dp1,1/0)\
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll N=2e5+5;
ll n,T;
ll dp[N][2],a[N];
int main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
//由于在越上面砍月优,所以砍下面时上面一定不砍或已经砍了,所以砍下面的不会对上面产生影响,可以考虑dp
//定义dp[i][1/0]为消灭区间[i,n],当前分不分裂的最少刀数cin>>T;while(T--){cin>>n;for(ll i=1;i<=n;i++){cin>>a[i];dp[i][1]=dp[i][0]=0x3f3f3f3f;//初值赋为极大值}dp[n+1][1]=dp[n+1][0]=0;dp[n+2][1]=dp[n+2][0]=0;//边界for(ll i=n;i>=1;i--){dp[i][0]=min(dp[i+1][0],dp[i+1][1])+a[i]-(i!=1);
//当前不砍则必定会受1点坠落伤害,1除外dp[i][1]=min(dp[i+2][0],dp[i+2][1])+max(a[i+1]-i,0ll)+a[i];
//砍死当前只会是i+1受到i的掉了伤害,其余的掉落伤害为1}cout<<min(dp[1][1],dp[1][0])<<'\n';}return 0;
}