CSP组T1怪物
CSP组T1怪物
题目大意
今有 n n n 只怪物,第 i i i 只怪物的生命值为 a a a。你和另外一位玩家正在合力击杀这些怪物,你的攻击力为 x x x,对方的攻击力为 y y y。你们需要轮流对活着的怪物进行攻击,第一个回合是你的回合。被攻击的怪物将会损失攻击它的玩家攻击力的生命值,若此后它的生命值非正,怪物将会死亡,击杀它的玩家得到 1 1 1 分。当不存在活着的怪物时,游戏结束。在你的回合,你可以选择攻击任意一只怪物,也可以选择不进行攻击。在对方的回合,对方会选择当前存活的,编号最小的怪物进行攻击。你需要合理地安排你的策略,使得你的得分最大,并求出这个得分。
思路过程
首先我一开始的思路就是普通的贪心吗?就是按价值排序,然后取最左边去杀,但是问题其实是在于这个问题?怪物一下砍不掉。就是说如果当前的最小你一剑砍不掉,对方也一剑砍不掉坐左边的怪兽,但是他砍完之后你再去砍就可以获得金币了,同理对方也是一样的道理,所以这道题的思路也就不是一个普通的贪心了。不过贪心的这个思路还是没有错的
设最终在第 i 只怪物上的得分为 x i ∈ 0 , 1 x_i \in {0,1} xi∈0,1,可以发现:
- 假如 x i = 0 x_i=0 xi=0,那么我们一定不会对第 i i i 只怪物进行任何攻击。
- 假如 x 1 = 1 x_1=1 x1=1,那么我们的攻击策略一定是:先进行次数尽量少的攻击,然后在对手攻击若干次后,最后将其一击毙命。
对于第 i 只怪物,你和对手的攻击次数分别为 p i , q i p_i,q_i pi,qi。对两种情况的攻击次数分别计算:
-
x i = 0 x_i=0 xi=0:完全让对手杀死这只怪物。
p i , 0 = 0 p_{i,0}=0 pi,0=0q i , 0 = ⌊ h i − 1 b ⌋ q_{i,0}=\left \lfloor \frac{h_i-1}{b} \right \rfloor qi,0=⌊bhi−1⌋
-
x i = 1 x_i=1 xi=1:进行 r r r 次的攻击后,满足 ( h i − r × a ) % b ∈ [ 1 , a ] (h_i−r \times a) \% b \in \left[1,a\right] (hi−r×a)%b∈[1,a],即 ( ( h i − 1 ) − r × a ) ∈ [ 0 , a ) \left(\left(hi−1\right)−r \times a\right) \in \left[0,a\right) ((hi−1)−r×a)∈[0,a),从中可以解得最小的 r r r。
p i , 1 = r + 1 = ⌊ ( h i − 1 ) % b a ⌋ + 1 p_{i,1}=r+1=\left \lfloor \frac{\left(h_i-1\right) \% b}{a} \right \rfloor +1 pi,1=r+1=⌊a(hi−1)%b⌋+1
q i , 1 = ⌊ ( h i − 1 ) − r × a b ⌋ q_{i,1}=\left \lfloor \frac{\left(h_i-1\right)-r \times a}{b} \right \rfloor qi,1=⌊b(hi−1)−r×a⌋
注意到我们可以跳过若干回合,且不必按顺序攻击,这意味着我们花费在前
i
i
i 只怪物上的攻击次数至多比对手多
1
1
1 次。记
d
i
,
k
=
q
i
,
k
−
p
i
,
k
d_{i,k}=q_{i,k}−p_{i,k}
di,k=qi,k−pi,k,则上述限制为:
∀
i
,
1
∑
k
=
1
i
(
[
x
k
=
0
]
d
k
,
0
+
[
x
k
=
1
]
d
k
,
1
)
≥
0
\forall i,1\sum_{k=1}^{i}\left(\left[x_k=0\right]d_{k,0}+\left[x_k=1\right]d_{k,1}\right) \ge 0
∀i,1k=1∑i([xk=0]dk,0+[xk=1]dk,1)≥0
问题转化为:对于每个
i
i
i 选择一个
x
i
=
0
/
1
x_i=0/1
xi=0/1 且使对应的前缀和不小于
0
0
0。由于每种选择方案的
d
i
d_i
di 都是已知的,可以贪心解决。具体地,假如选择
x
i
=
1
x_i=1
xi=1 后仍然满足条件,那么直接使答案加
1
1
1;否则贪心地将之前(包括当前的
i
i
i)选择
x
j
=
1
x_j=1
xj=1 且
d
j
,
0
−
d
j
,
1
d_{j,0}−d_{j,1}
dj,0−dj,1 最大的
j
j
j 改为
x
j
=
0
x_j=0
xj=0(由于
d
i
,
0
d_{i,0}
di,0 必然e大于
0
0
0,所以调整之后一定可以满足限制)。整个过程可以使用优先队列维护。
时间复杂度: O ( n log n ) \mathcal{O}(n \log n) O(nlogn)。
代码部分
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, x, y, a[5000005], yes[5000005], no[5000005];
int ans,sum=1;
signed main()
{
cin>>n>>x>>y;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
a[i]--;
int p=(a[i]%y)/x,q=(a[i]-p*x)/y;
p++;
yes[i]=q-p;
no[i]=a[i]/y+1;
}
priority_queue<int>monster;
for (int i=1;i<=n;i++)
{
monster.push(no[i]-yes[i]);
sum+=yes[i];
if (sum>=0)
{
ans++;
}
else
{
sum+=monster.top();
monster.pop();
}
}
cout<<ans<<endl;
return 0;
}