A.Era
题目描述
给一个长为 nnn 的序列 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an,你可以对序列做任意次如下所述的操作:选择一个数字 kkk(每次操作选择的数字可以不同),将其插入到序列 aaa 的任意位置,比如:a=[3,3,4]a=[3,3,4]a=[3,3,4],k=2k=2k=2,操作后可能的序列有 [2,3,3,4],[3,2,3,4],[3,3,2,4],[3,3,4,2][2,3,3,4],[3,2,3,4],[3,3,2,4],[3,3,4,2][2,3,3,4],[3,2,3,4],[3,3,2,4],[3,3,4,2]。
操作尽可能少的次数,使序列 aaa 满足:∀i,ai≤i\forall i,a_i\leq i∀i,ai≤i,求这个最少的操作次数。
数据范围:多组测试数据,1≤n≤1001\leq n\leq 1001≤n≤100,1≤ai≤1091\leq a_i\leq 10^91≤ai≤109。
分析
对于某个下标 iii,如果 ai>ia_i>iai>i,最少需要在位置 iii 前插入 ai−ia_i-iai−i 个数字。枚举所有的 ai−ia_i-iai−i,其中的最大值即为答案,即 ans=maxi=1n{ai}ans=\max\limits_{i=1}^{n}\{a_i\}ans=i=1maxn{ai}。
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
int ans = 0;
for (int i = 1; i <= n; i++) {
int k;
scanf("%d", &k);
ans = max(ans, k - i);
}
cout << ans << endl;
}
}
B.XOR Specia-LIS-t
题目描述
给一个长为 nnn 的序列 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an,把它分成若干个不相交的子序列,第 iii 个子序列的最长递增子序列的长度定义为 hih_ihi。比如我们把序列 [2,5,3,1,4,3,2,2,5,1][2,5,3,1,4,3,2,2,5,1][2,5,3,1,4,3,2,2,5,1] 分割成 [2,5,3,1,4],[3,2,2,5],[1][2,5,3,1,4],[3,2,2,5],[1][2,5,3,1,4],[3,2,2,5],[1],则 h=[3,2,1]h=[3,2,1]h=[3,2,1]。
判断能否以某种方式分割 aaa 序列,使得 hhh 序列的异或和等于 000。
数据范围:多组测试数据,2≤n≤1052\leq n\leq 10^52≤n≤105,1≤ai≤1091\leq a_i\leq 10^91≤ai≤109,nnn 的总和不超过 3×1053\times 10^53×105。
分析
如果 nnn 是偶数,把 aaa 序列分割成长为 111 的 nnn 个子序列,答案一定是 YES。
如果 nnn 是奇数,我们考虑寻找满足 ai≥ai+1a_i\geq a_{i+1}ai≥ai+1 的相邻的两个数字,把这两个数字作为一个子序列分割出来,最长递增子序列的长度为 111,如果找的到,答案为 YES,否则为 NO。
代码
#include<bits/stdc++.h>
using namespace std;
int a[300010];
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
bool flag = true;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for (int i = 1; i <= n - 1; i++)
if (a[i] >= a[i + 1]) {
flag = false;
}
if (n % 2 == 0 || flag == false) {
puts("YES");
} else {
puts("NO");
}
}
return 0;
}
C.Di-visible Confusion
题目描述
给一个长为 nnn 的序列 a1,a2,⋯ ,ana_1,a_2,\cdots,a_na1,a2,⋯,an, 进行如下操作:选择一个 aia_iai,要求 (i+1)∤ai(i+1)\nmid a_i(i+1)∤ai,将其从序列中移除。判断能否通过若干次操作使得序列变为空。
数据范围:多组测试数据,1≤n≤1051\leq n\leq 10^51≤n≤105,1≤ai≤1091\leq a_i\leq 10^91≤ai≤109,nnn 的总和不超过 3×1053\times 10^53×105。
分析
对于所有的 iii,如果 222 ~ i+1i+1i+1 中至少存在一个数字不整除 aia_iai,则答案为 YES,否则答案为 NO。
使用数学归纳法来证明:如果 ana_nan 前面的 n−1n-1n−1 个数字都能被移除,则 ana_nan 也能在其位置为 $1 $ ~ nnn 中的某个位置时被移除(假设该位置为 kkk,ana_nan 可以在序列中有 k−1k-1k−1 个数字时被移除)。
下面考虑如何快速判断 222 ~ $ i+1$ 中是否至少有一个数字不整除 aia_iai。如果 aia_iai 能被 222 ~ $ i+1$ 的所有数字整除,这代表 aia_iai 也能被 LCM(2,3,⋯ ,i+1)\text{LCM}(2,3,\cdots,i+1)LCM(2,3,⋯,i+1) 整除,但是当 n=22n=22n=22 时,LCM(2,3,⋯ ,23)>109>ai\text{LCM}(2,3,\cdots,23)>10^9>a_iLCM(2,3,⋯,23)>109>ai。所以当 i≥22i\geq 22i≥22 时,必然存在一个数字不整除 aia_iai,不需要逐一判断;当 i<22i<22i<22 时,暴力检验。时间复杂度 O(n+212)O(n+21^2)O(n+212)。
代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
bool flag = true;
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
bool f = false;
for (int j = i + 1; j >= 2; j--) {
if (x % j != 0) {
f = true;
break;
}
}
flag = flag & f;
}
if (flag == true) {
puts("YES");
} else {
puts("NO");
}
}
return 0;
}
D.Moderate Modular Mode
题目描述
有两个偶数 x,y(2≤x,y≤109)x,y(2\leq x,y\leq 10^9)x,y(2≤x,y≤109),找到一个数字 n(2≤n≤1018)n(2\leq n\leq 10^{18})n(2≤n≤1018),使得 nmod x=ymod nn\mod x=y\mod nnmodx=ymodn。
分析
如果 x>yx>yx>y,则 (x+y)mod x=ymod x=ymod (x+y)=y(x+y)\mod x=y\mod x=y\mod (x+y)=y(x+y)modx=ymodx=ymod(x+y)=y,即 n=x+yn=x+yn=x+y。
如果 x≤yx\leq yx≤y:
结论一:n≥xn\geq xn≥x。
证明:使用反证法证明,存在某个 n<xn<xn<x 使得 nmod x=ymod nn\mod x=y\mod nnmodx=ymodn 成立。则 nmod x=nn\mod x =nnmodx=n,但 ymod n<ny\mod n<nymodn<n,与假设矛盾,因此结论一正确。
结论二:n≤yn\leq yn≤y。
证明:使用反证法证明,存在某个 n>yn>yn>y 使得 nmod x=ymod nn\mod x=y\mod nnmodx=ymodn 成立。则 nmod x<xn\mod x<xnmodx<x,但 ymod n=y≥xy\mod n=y\geq xymodn=y≥x,与假设矛盾, 因此结论二正确。
所以 x≤n≤yx \leq n \leq yx≤n≤y,下面考虑如何求出 nnn 确切的值。
假设有一个 XXX 轴,一开始在 000 点,从 000 点跳到 yyy 点,每次跳跃的距离为 xxx。有可能在最后一次跳跃后跳过点 yyy,设最后一次跳跃前的位置为 p=y−ymod xp=y-y\mod xp=y−ymodx,从位置 ppp 跳跃到 yyy 点需要两个步骤。因为 y−py-py−p 是偶数,所以我们需要跳跃 y−p2\frac{y-p}{2}2y−p 个单位,此时跳到了位置 t=p+y−p2t=p+\frac{y-p}{2}t=p+2y−p,可以发现 ttt 就是我们要求的 nnn,因为 tmod x=y−p2t\mod x=\frac{y-p}{2}tmodx=2y−p 且 ymod t=(y−p)−y−p2=y−p2y\mod t=(y-p)-\frac{y-p}{2}=\frac{y-p}{2}ymodt=(y−p)−2y−p=2y−p,即 n=t=y−ymod x2n=t=y-\frac{y\mod x}{2}n=t=y−2ymodx。

代码
#include<bits/stdc++.h>
using namespace std;
int main() {
int T;
cin >> T;
while (T--) {
int x, y;
cin >> x >> y;
if (x <= y) {
cout << y - y % x / 2 << endl;
} else {
cout << x + y << endl;
}
}
return 0;
}
E.Extreme Extension
题目描述
给一个长为 n(1≤n≤105)n(1\leq n\leq 10^5)n(1≤n≤105) 的序列 a1,a2,⋯ ,an(1≤ai≤105)a_1,a_2,\cdots,a_n(1\leq a_i\leq 10^5)a1,a2,⋯,an(1≤ai≤105),每次操作可以选择序列中的一个数字 aia_iai,将其拆分为两个数字之和并代替 aia_iai 加入到序列中。定义一个序列的 极值 为使得数组单调不下降所需的最少操作次数,求序列 aaa 的所有子序列的 极值 之和,答案对 998244353998244353998244353 取模。
分析
对于 ai>ai+1a_i>a_{i+1}ai>ai+1 的情况,需要将 aia_iai 拆成 kkk 个数字:1≤b1≤b2≤⋯≤bk≤ai+11\leq b_1\leq b_2\leq \cdots\leq b_k\leq a_{i+1}1≤b1≤b2≤⋯≤bk≤ai+1 且 b1+b2+⋯+bk=aib_1+b_2+\cdots+b_k=a_ib1+b2+⋯+bk=ai。由于 bk≤ai+1b_k\leq a_{i+1}bk≤ai+1,所以 k≥⌈aiai+1⌉k\geq \lceil\frac{a_i}{a_{i+1}}\rceilk≥⌈ai+1ai⌉ 。显然 b1b_1b1 越大操作的次数就越少(比如 [4,4,4,5][4,4,4,5][4,4,4,5] 比 [3,3,3,3,5][3,3,3,3,5][3,3,3,3,5] 要好),所以令 k=⌈aiai+1⌉k=\lceil\frac{a_i}{a_{i+1}}\rceilk=⌈ai+1ai⌉。注意到 b1≤⌊aik⌋b_1\leq \lfloor\frac{a_i}{k}\rfloorb1≤⌊kai⌋,令 b1=⌊aik⌋=⌊ai⌈aiai+1⌉⌋b_1=\lfloor\frac{a_i}{k}\rfloor=\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{a_{i+1}}\big\rceil}\Bigg\rfloorb1=⌊kai⌋=⌊⌈ai+1ai⌉ai⌋。经过 k−1k-1k−1 次操作即可将 aia_iai 拆分成 [b1,b2,⋯ ,bk][b_1,b_2,\cdots,b_k][b1,b2,⋯,bk]。
按照如下步骤求出 极值:
- 倒序遍历 i=n−1i=n-1i=n−1 ~ 111。
- 答案加 ⌈aiai+1⌉−1\lceil\frac{a_i}{a_{i+1}}\rceil-1⌈ai+1ai⌉−1。
- 令 ai=⌊ai⌈aiai+1⌉⌋a_i=\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{a_{i+1}}\big\rceil}\Bigg\rfloorai=⌊⌈ai+1ai⌉ai⌋
时间复杂度为 O(n)O(n)O(n),求出所有子序列的 极值 之和的时间复杂度为 O(n2)O(n^2)O(n2),考虑如何用更优地时间复杂度解决此题。
令 dp(i,x)dp(i,x)dp(i,x) 为第 iii 个数被拆分为最小元素为 xxx 的若干个数,而且以 xxx 为开头的非递减数列的数量。
我们只考虑 xxx 有多少种可能的值(实际只有 O(105)O(\sqrt{10^5})O(105),后面会提到)。对于 x=1x=1x=1 ~ 10510^5105,需要进行如下的状态转移:dp(i,⌊ai⌈aix⌉⌋)+=dp(i+1,x)dp\Bigg(i,\Bigg\lfloor\frac{a_i}{\big\lceil\frac{a_i}{x}\big\rceil}\Bigg\rfloor\Bigg)+=dp(i+1,x)dp(i,⌊⌈xai⌉ai⌋)+=dp(i+1,x)。
实际上 xxx 最多只有 21052\sqrt{10^5}2105 种不同的值(⌊m1⌋,⌊m2⌋,⋯ ,⌊mm⌋\lfloor\frac{m}{1}\rfloor,\lfloor\frac{m}{2}\rfloor,\cdots,\lfloor\frac{m}{m}\rfloor⌊1m⌋,⌊2m⌋,⋯,⌊mm⌋,整除分块的思想)。因此我们可以在 O(n105)O(n\sqrt{10^5})O(n105) 的时间复杂度内解决此题,而且转移方程之和 i,i+1i,i+1i,i+1 有关,可以使用滚动数组优化掉一维。
最后根据 dpdpdp 数组计算答案,对于 dp(i+1,x)dp(i+1,x)dp(i+1,x),它对答案的贡献为 i×dp(i+1,x)×(⌈aiai+!⌉−1)i\times dp(i+1,x)\times(\lceil\frac{a_i}{a_{i+!}}\rceil-1)i×dp(i+1,x)×(⌈ai+!ai⌉−1),这是因为有 i×dp(i+1,x)i\times dp(i+1,x)i×dp(i+1,x) 个序列,每个序列都对 aia_iai 进行这样的拆分。
时间复杂度 O(n105)O(n\sqrt{10^5})O(n105),空间复杂度 O(n)O(n)O(n)。
代码
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
const int mod = 998244353;
int a[300010];
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
gp_hash_table<int, int> dp;
long long ans = 0;
for (int i = n; i >= 1; i--) {
gp_hash_table<int, int> &&dp2 = {};
dp2[a[i]] = 1;
for (auto it:dp) {
int t = (a[i] + it.first - 1) / it.first;
dp2[a[i] / t] += it.second;
ans = ans + ((long long) i * it.second) * (t - 1);
}
ans = ans % mod;
dp = move(dp2);
}
cout << ans << endl;
}
return 0;
}
本文详细解析了Codeforces Round #752 (Div. 2)中的五道编程题目,包括A.Era、B.XOR Specia-LIS-t、C.Di-visible Confusion、D.Moderate Modular Mode和E.Extreme Extension。通过对题目描述、问题分析及代码实现的阐述,帮助参赛者理解解题思路和优化方法。
1639

被折叠的 条评论
为什么被折叠?



