SCOI2024day1t3重整山河 题解

我去我好像做不太懂这道题,有无大手子教教/ll


题意

有 \(n\) 只怪兽,每只怪兽有两个属性 \((a_i,b_i)\)。其中 \(a_i\) 表示生命值,\(b_i\) 表示爆炸伤害。每次你选择一只怪兽攻击;一只怪兽死亡后会对所有怪兽造成 \(b_i\) 点伤害。怪兽被攻击/生命值降为 \(0\) 则立刻死亡。需要求出最小攻击次数。

但是现在只给定 \(a_i\),只知道 \(b_i \in l_i,r_i\)。你需要对所有 \(b\) 序列,求出最小攻击次数的和。

对 \(M\) 取模。

\(1 \le n \le 20,1 \le l_i \le r_i \le 15,1 \le a_i \le 300,2 \le M \le 10^9+7\)。

解法

1

考虑 \(b\) 序列固定,如何算答案。

注意到爆炸的先后顺序对答案没有影响。所以你不妨化身 JOJO,来一手神之时停,钦定一个攻击集合 \(S\),攻击完一个人让它先别炸,等全部攻击完最后一起炸。

然后我们按 \(a_i\) 升序排序,则最终死亡怪兽必然是一个前缀。假设我们从前往后依次考虑每个怪兽是否能够死亡,怪兽 \(i\) 能死亡等价于 \(i \in S \lor \sum_{j=1}^{i-1}b_j+\sum_{t=i+1}^{n}b_tt \\in S \ge a_i\)。

时间复杂度 \(O(2^nn)\)。

但是这太区了。经验告诉我们,想要计数,起码解决子问题的复杂度要是多项式的。

注意到上式中我们只关心后缀 \(S\) 中 \(b\) 的和。所以不妨从后往前依次考虑每个元素是否属于 \(S\):如果它满足上式,则你不攻击它它也能自然死亡,所以我们先不攻击它;否则出现了两个选择:攻击它,或攻击后缀尚未被攻击的一些怪兽使得它满足上式子。

记 \(mx\) 表示后缀尚未被攻击的怪兽的 \(b_i\) 的最大值。我们进行一个分讨:若 \(b_i \ge mx\),你攻击 \(a_i\) 既获得最大 \(b_i\),又达到了理论最小攻击次数 \(1\),所以攻击 \(a_i\) 是最优的;否则 \(b_i \lt mx\),考虑 \(mx\) 所属的怪兽 \(j\),其生命值和爆炸伤害都偏序怪兽 \(i\),我们肯定优先攻击怪兽 \(j\)。并且可以证明,攻击完怪兽 \(j\) 后 \(i\) 一定满足上式。(感性理解就是攻击 \(j\) 不劣于攻击 \(i\),而攻击 \(i\) 的新增攻击次数是 \(1\),理性证明就是考虑 \(i\) 右边第一个未被攻击的怪兽(一定存在),考虑二者式子左边的差,发现至多为 \(b_i\),而我们现在新增 \(b_j \gt b_i\),且 \(a_i\) 还变小了,必然满足限制。)

注意到对于后缀未攻击的怪兽,我们不关心具体位置,只关心 \(b_i\) 构成的集合,并且每次我们都会按 \(b_i\) 从大往小攻击,所以可以用大根堆维护。

所以我们得到做法:维护 \(s\) 表示 \(b\) 的前缀和。从后往前扫,用大根堆维护未决策 \(b_i\),记 \(now\) 表示已决策 \(b_i\) 和。每次先将 \(b_i\) 加入大根堆,然后如果 \(s_{i-1}+now \ge a_i\) 不成立,则决策堆顶元素并弹出。时间复杂度 \(O(n \log n)\)。

cpp 复制代码
std::priority_queue<int> q;
FOR(i,1,n)(bs[i]=bs[i-1])+=b[i];
int ct = 0,now = 0;
REP(i,n,1){
	q.push(b[i]);
	if(bs[i-1]+now<a[i].a)now+=q.top(),q.pop(),adx(ct,1);
}

2