比赛地址
https://gmoj.net/senior/#contest/home/3245
题目
6843. 【2020.11.02提高组模拟】移形换影
6844. 【2020.11.02提高组模拟】旅途和生活
6845. 【2020.11.02提高组模拟】梯度弥散
6846. 【2020.11.02提高组模拟】旅人1970
总结
这次会做前3题(准确来说是2.5题,因为T2的结论不会证明),然而却只有184分,只切了T3,后来发现犯了不少低级错误。
T1
一道繁琐的模拟题。为了方便处理,可以只把0和2放进序列里,把1挂在后面的数字上,后面的数字只用记录前面1的个数就好了;此外,还要在结尾多放一个2(如果是0,就有可能会被拉到前面去)。
比如说序列 1 0 0 2 1 0 1 1 2 0 1 1\, 0\, 0\, 2\, 1\, 0\, 1\, 1\, 2\, 0\, 1 10021011201会变成 1 0 0 2 1 0 2 2 0 1 2 _10\, 0\, 2\, _10\, _22\, 0\, _12 10021022012。
接着从左往右扫。如果当前数字是2,就把后面离它最近的1拉到它前面,拉不动就算了;如果当前数字是0,就把它前面的1移到它后面的第一个2的前面,就算移不过去也要移动。
结果WA 60了……
后来发现是处理0时有问题:
2
00002
_200002
200002在动2步后最优的情况应该是
0
2
0002
0_20002
020002而不是
1
0
0
1
002
_100_1002
1001002!
因此如果当前数字是0,直接把它前面的1搬到后面就行了(每次只把它自己和前面的1交换)。
T2
本来是没什么思路的。但是我看到了这句话:
而上表说3,4,5,6保证a和b都是奇数(证明在https://blog.csdn.net/huangzihaoal/article/details/109477724)。
我们可以先把a和b都写成
2
x
y
2^xy
2xy的形式,使得它们中至少有一个的y是奇数。接下来分类讨论:
- 当a和b都是奇数时,根据题目给出的结论,可以只存下
a
n
a^n
an和
b
n
b^n
bn最后的63个二进制位。因此用
unsigned long long
搞快速幂就好了; - 当a和b一奇一偶时, l o w b i t ( a n − b n ) lowbit(a^n-b^n) lowbit(an−bn)显然是1。
最后把 lowbit 的结果乘上 2 n x 2^{nx} 2nx就好了。
但是出乎意料的是我WA 20了。
经检查,发现我用unsigned long long
(用ll
表示)做的快速幂是这样打的:
inline ll pow2(ll x,int y)
{
int s=1;//这里用了int,不炸才怪
while(y)
{
if(y&1) s*=x;
x*=x,y>>=1;
}
return s;
}
联赛时如果犯了这种低级错误估计又要赛季报销了。
T3
不难想到要二分答案。
看到
c
∈
0
,
1
,
2
c\in{0,1,2}
c∈0,1,2,想到分类讨论。
先考虑最复杂的
c
=
2
c=2
c=2的情况,假设现在有p次攻击,它们的攻击力为
a
1
,
a
2
,
⋯
,
a
p
a_1,a_2,\cdots,a_p
a1,a2,⋯,ap,那么经过到下一个敌人时,它们的总攻击力
∑
i
=
1
p
a
i
′
2
=
∑
i
=
1
p
a
i
2
−
2
(
∑
i
=
1
p
a
i
)
+
p
−
q
\sum_{i=1}^p a_i'^2=\sum_{i=1}^p a_i^2 -2\left(\sum_{i=1}^p a_i\right)+p-q
i=1∑pai′2=i=1∑pai2−2(i=1∑pai)+p−q
q表示在打完这次后伤害变成0的a的数量。
可以令 s 1 = ∑ i = 1 p a i 2 , s 2 = ∑ i = 1 p a i , s 3 = p s1=\sum_{i=1}^p a_i^2,s2=\sum_{i=1}^p a_i,s3=p s1=∑i=1pai2,s2=∑i=1pai,s3=p,这样就能存下这个值了。接着再用一个数组记录当前位置的 q q q即可。
c = 1 , c = 0 c=1,c=0 c=1,c=0的情况类似(移除最高次项就好了)。
还好,这题没有打挂,AC了。
T4
考场上没什么时间做,只拿了4分。
这是一道结论题。
结论1
在最优的划分中,某个众数只会出现至多一次,否则显然可以将相同的众数所在的集合合并来减小答案。
结论2
若将某些数字选为最后的众数,这个划分方案合法当且仅当没有选为众数的数,它们的个数的最大值小于等于选出的这些众数的个数之和。
这样一来,我们就可以得到一个
n
q
nq
nq的做法:对于每次询问,首先找到一个最短的前缀使得a[i]的和大于等于V,其中V表示a[i]的最大值。然后从后往前把某些数字删掉:若删去数字a[i]后总和仍大于等V,那么就删掉。
结论3
所有删去的数字形成的连续段数不超过
V
\sqrt{V}
V。
证明:
对于某个删去的数字,若它右边的数字没有被删掉,那么右边的这个数字一定大于前面的删去的数字之和,否则它就会被删去。而未被删去的数字之和大于等于V且小于2V(由值域决定),而删去的数字在其中共出现了平方次,因此删去的数字形成的连续段只有根号级别。
这样,为了优化在序列上暴力找删除的连续段,我们改为在线段树上暴力找删除的连续段:
若删去当前节点中的最小值仍不能满足条件,直接返回;若将右节点整个删去仍满足条件,那么将其删去并遍历左节点;否则遍历先右节点再遍历左节点。
总复杂度
O
(
n
V
log
2
n
)
O(n\sqrt{V} \log_2n)
O(nVlog2n),实际上很难卡满,期望得分100。
总结与反思
虽然这次题目总体难度不大,我会做3道题,但是第一题思考得不完善,第二题犯了一个极其低级的错误,实在不应该!
打代码前一定要三思,打完后不要急着测样例,而是用眼睛查错。