The 2021 ICPC Asia Nanjing Regional Contest (GYM103470)
A
签到题,略。
C
Description
给定一个长度为\(n\)序列和一个整数\(k\),你可以选择给序列的某一段加上\(k\),要求最大化众数的出现次数。
\(n\le 10^6\), \(-10^6 \le a_i, k \le 10^6\)
Solution
发现权值范围不大,想到对于权值进行枚举。每次枚举\(x,x+k\),找到所有\(x\)和\(x+k\)的位置,我们需要在其中选择一段将\(x\)变成\(x+k\)、\(x+k\)变成\(x+2k\)。\(+k\)前的答案即为\(x+k\)的出现次数。将\(x\)视为\(1\)、\(x+k\)视为\(-1\),题目转化为求这个序列的最大子段和,加上\(x+k\)的出现次数即为\(+k\)之后\(x+k\)最多可能的出现次数。
注意\(k=0\)的情况和最后答案不需要\(+k\)的情况。
D
Description
给定一段代码:
for (int i = 1; i <= n; ++ i)
for (int j = 1; j <= n; ++ j)
if (a[i] < a[j]) swap(a[i], a[j]);
给定序列\(a_i\),问对于\(a_i\)的每一个前缀,执行这段代码的swap
次数。
(\(\sum n\le 10^6\))
Solution
在进行了第一轮交换之后,相当于一个极长上升子序列进行了轮换,之后的第\(i\)轮交换会保证前\(i\)个数是递增的,故第\(i+1\)轮的交换次数即为前\(i\)个数里比\(a_{i+1}\)大的数里有多少种不同的数。
- 对于不属于极长上升子序列中的数,在第一轮交换前后的左边比它大的数的个数是不变的;
- 对于属于该子序列中的数,在第一轮会进行一次交换,在它所属的那一轮也会进行一次交换,所以对答案有2的贡献。
如果存在相同数字,则需另外考虑:
- 一方面,对于等于极长上升子序列中某个数\(a_i\)的数\(a_j\),在原序列上考虑前面大于\(a_j\)的数的个数时并不会考虑到\(a_i\),但是在进行第一轮交换之后,\(a_i\)会被换到后面去,取而代之的是全序列最大的那个数;
- 另一方面,若\(a_i = a_j\)且\(i<j\),对于\(k>j\)且\(a_k<a_j\),我们统计有多少个不同的数大于\(a_k\)时,\(a_i\)和\(a_j\)被看作了同一个,但是进行了第一轮交换之后这两个位置都会有贡献。
E
Description
有一个长度为\(n\)的序列,对其进行\(m\)个操作,每次将\(a_{l_i},\dots, a_{r_i}\)加上\(x_i\)。我们设第\(j\)次操作后,\(a_i\)的值为\(a_{i,j}\)。给出\(q\)个询问\(l_i,r_i,x_i,y_i\),要求算出:
答案对\(10^9+7\)取模。(\(n,m,q\le 5\times 10^4\))
Solution
我们对于每个询问进行拆解,变成\(1,l_i-1,x_i,y_i\)和\(1,r_i,x_i,y_i\)两个询问分别挂在对应的时间点上。我们按时间顺序依次处理每个操作,问题变成如何维护区间历史版本平方和。
考虑用线段树维护,对于当前时间点\(t\)和一个线段树节点表示的区间\([l,r]\),我们设:
设时间点\(t-1\)的区间信息对应为\(A',B',C',D'\),时间点\(t\)时这个区间加上了\(x\),那么有:
将转移表示成矩阵的形式,即:
对于线段树,维护\(A,B,C,D\)和一个矩阵tag即可。(注意,无论是否修改,每个点都需要累加历史版本和,所以需要另外添加\((0,l_i-1,0)\)和\((r_i+1,n,0)\)两组修改)
一些常数优化:两个对角线为1上三角矩阵相乘的结果仍然时对角线为1的上三角矩阵,所以需要考虑的只有右上角6个数。
启发:
-
解决线段树历史版本的一个新的思路;
-
对于线段树维护多个量且量与量之间有线性转移关系的问题,可以考虑用矩阵乘法解决。
H
Description
给定一棵以1为根的树,每个节点有一个权值\(a_i\)。从\(1\)开始遍历这棵树,每到达一个节点,它周围的点的权值在\(t_i\)个单位时间内就会消失,我们希望取到最大权值和。
\(\sum n\le 10^6, 1\le t_i\le 3\)
Solution
发现\(t_i\)最大只有3,也就是说我们最多允许向某一个子节点走了一步后立刻返回、然后踩点到达另外一个\(t_i\)为3的子节点。
令\(f_u\)表示不考虑节点\(u\)本身,子树内可以取到的最大权值是多少。
对于每个节点\(u\)有两种选择:
-
选择一个子节点\(v\),并往该子树走,此时相当于可以取到\(v\)的权值而放弃了\(u\)的其它子节点的权值
\[f_u = s_u + \max_v a_v \] -
选择一个子节点\(v_1\),走到该位置后立刻返回,然后选择另外一个子节点\(v_2\),并往该子树走,此时相当于取到了\(v_1\)和\(v_2\)而放弃了\(u\)的其它子节点和\(v_1\)的其它子节点
\[f_u = s_{v_1} + a_{v_1} + (s_u - f_{v_1}) + a_{v_2} \]
(其中\(s_u = \sum_v f_v\))
最后的答案即为\(f_1+a_1\)。
I
Description
平面直角坐标系中有\(y=0\)和\(y=H\)两条边界,有\(n\)个障碍,坐标为\((x_i,y_i)\);有\(m\)个金币,坐标为\((x_i', y_i')\)。从\((0,0)\)出发,初始方向为\(\vec{v} = (1,1)\),如果碰到边界或者木桩,\(v_y\)变成相反数。
你可以选择删掉一些木桩,问最多可以拿到多少个金币。
\(\sum n, \sum m \le 5\times 10^5\)
Solution
在没有木桩的情况下,\((x-y+2H)\bmod 2H = k\)且方向为\((1,1)\)的状态以及\((x+y) \bmod 2H = k\)且方向为\((1,-1)\)的状态是同一条路径上的,一共有\(2H\)条不同的路径。
我们以横坐标以及在哪一条路径上表示状态来进行转移(即对于每个横坐标,不同的纵坐标和两个方向组合起来就是\(2H\)条不同的路径)。
若状态所在坐标存在金币,则答案+1;若状态所在坐标存在障碍,也就是说可以选择这个位置是否转向,那么处于这个位置的两条路径都可以取到二者的较大值。
(不要忘了只能从\((0,0)\)出发)
J
Description
给定二元组\((a,b)\),有以下三种操作:
- \(a,b\)同时加\(1\)
- \(a,b\)同时减\(1\)
- \(a,b\)同时除一个质因子
要求通过上述操作将\(a,b\)中的其中一个变成\(1\),求最少操作次数。
\(a,b\le 10^9\)
Solution
不妨假设\(a<b\)。
易知每次除的质因子一定是\(b-a\)的引子。我们枚举除因子的顺序,每次如果不能整除,就通过+1或者-1来调整。
我们考虑\(a\)和\(b-a\)表示搜索的状态,\(b-a\)一共有因子个数个;若已知\(b-a\),我们就知道\(a\)被哪些质因子除过,而且要么是除以这些因子之积向下取整,要么是向上取整,所以总状态数也是因子个数个的,故暴搜可过。
M
签到题,注意\(n=1\)的情况和所有\(a_i\)全部相等、最大值最小值指定为同一个位置的情况。