0%

题目大意

这是问题的简单版本。版本之间的区别在于xx的约束;在这个版本中,x105x \le 10^5

Polycarp有一个包含从11101210^{12}的所有自然数的序列。他决定通过执行以下操作$ x $次来修改这个序列:

  • 同时删除所有在位置yy2y2 \cdot y3y3 \cdot y,…,mynm \cdot y \le n的数字,其中$ n $是当前序列的长度。

之后,Polycarp想要在剩余序列中找到第$ k 个数字,或确定结果序列的长度小于个数字,或确定结果序列的长度小于 k $。

帮助Polycarp解决这个问题!

考虑一个例子。假设$ x = 2 y = 3 k = 5 $,那么:

image

红线划掉的数字在第一次操作后被删除,蓝线划掉的数字在第二次操作后被删除。因此,位置$ k = 5 处的数字是数字处的数字是数字 10 $。

思路讲解


我们直接使用这个第一问当中的这个 check 函数,由于其是 O(X)O(X),会超时。我们也可以优化这个二分检查器代码,让其一次性把 subtractlenysubtract\gets \lfloor \frac{len}{y}\rfloor 相同的一起处理。这样子,使用二分的复杂度就是 O(AlogA)O(\sqrt A \log A),其中 A\sqrt A 是一个数字能够拥有的这个因数数量量级。

不过题目作者专门卡了我们二分答案一手,因此必须采用逆向还原的思路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (ll i=1;i<=X;++i) {
ll subtract=0;
if (len%(Y-1)==0) {
subtract=len/(Y-1)-1;
len+=subtract;
}else {
subtract=len/(Y-1);
ll dis=((Y-1)*(subtract+1))-len;
ll cnt=(dis+subtract-1)/subtract;
cnt=min(X-i+1,cnt);
i+=cnt-1;
len+=cnt*subtract;
}
if (len>(ll)1e12) {
cout<<-1<<"\n";
return;
}
}

逆向还原,我们知道,每出现 y1y-1 个数字,那么就代表有一个数字被删除了,那么一次需要增长的长度就是 leny1\lfloor \frac{len}{y-1} \rfloor

这里有一个比较阴的特殊情况,就是 len0(mody1)len \equiv 0 \pmod {y-1},这个时候,能够增长的长度就是只有 leny11\lfloor \frac{len}{y-1} \rfloor-1

我们可以就像最简单的,当序列长度为 y1y-1,其达到 y1y-1 需要多少长度,是 yy 吗?显然不是,其需要增长的就是 0 长度。

我们和优化二分的时候采用一样的思路,每次增长相同长度的,一起操作,这样子时间复杂度就是 O(A)O(\sqrt A)

具体而言,就是先计算需要增长的长度 subtractsubtract,然后计算需要能够以 subtractsubtract 继续增长的次数,即 cntcnt,然后 cntmin(cnt,Xi+1)cnt\gets \min(cnt,X-i+1),避免加上 cntcnt 超过操作次数限制。不断重复这个过程,直到达到操作次数 XX

AC代码

AC
https://codeforces.com/contest/2169/submission/360686378

TLE

https://codeforces.com/contest/2169/submission/360461585

心路历程(WA,TLE,MLE……)

题目大意

这是问题的简单版本。版本之间的区别在于xx的约束;在这个版本中,x105x \le 10^5

Polycarp有一个包含从11101210^{12}的所有自然数的序列。他决定通过执行以下操作$ x $次来修改这个序列:

  • 同时删除所有在位置yy2y2 \cdot y3y3 \cdot y,…,mynm \cdot y \le n的数字,其中$ n $是当前序列的长度。

之后,Polycarp想要在剩余序列中找到第$ k 个数字,或确定结果序列的长度小于个数字,或确定结果序列的长度小于 k $。

帮助Polycarp解决这个问题!

考虑一个例子。假设$ x = 2 y = 3 k = 5 $,那么:

image

红线划掉的数字在第一次操作后被删除,蓝线划掉的数字在第二次操作后被删除。因此,位置$ k = 5 处的数字是数字处的数字是数字 10 $。

思路讲解

二分答案。

思路参考了A_G 的代码

将这道题目转化为二分答案的关键,就在于把找到剩余的第 $ k $ 个数字,转化为找到一个长度为 lenlen 的初始序列,使得其剩余至少 kk 个数字,那么不难发现,最小的,符合我们的要求的 lenlen 就是答案。

那么,一次操作会减少多少个数字(长度为 lenlen 时)? 其实就是 leny\lfloor \frac{len}{y}\rfloor

因此,简单版本,检查函数也不难书写,直接模拟改过程即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto check=[&](ll len) {
ll cnt=1;
while (cnt<=X) {
if (len<=0) {
break;
}
len-=len/Y;
++cnt;
}
if (len>=K) {
return true;
}
return false;
};

AC代码

AC
https://codeforces.com/contest/2169/submission/360432707

心路历程(WA,TLE,MLE……)

题目大意

CF2169C Range Operation

题目描述

给定一个长度为 nn 的整数数组 aa

你可以进行以下操作:选择一个区间 [l,r][l, r]1lrn1 \le l \le r \le n),并将区间内的元素 al,al+1,,ara_l, a_{l+1}, \dots, a_r 全部替换为 l+rl + r

你的任务是计算,在最多允许进行一次上述操作的情况下,数组的和可能达到的最大值。

输入格式

第一行包含一个整数 tt1t1041 \le t \le 10^4),表示测试用例的数量。

每个测试用例的第一行包含一个整数 nn1n21051 \le n \le 2 \cdot 10^5)。

第二行包含 nn 个整数 a1,a2,,ana_1, a_2, \dots, a_n0ai2n0 \le a_i \le 2n)。

输入额外限制:所有测试用例中 nn 的总和不超过 21052 \cdot 10^5

输出格式

对于每个测试用例,输出一个整数,表示在至多进行一次操作后数组的最大和。

输入输出样例 #1

输入 #1

1
2
3
4
5
6
7
8
9
10
4
3
2 5 1
2
4 4
4
1 3 2 1
5
3 2 0 9 10

输出 #1

1
2
3
4
5
13
8
20
32

说明/提示

在第一个样例中,可以对子数组 [3,3][3,3] 执行操作,得到数组 [2,5,6][2, 5, 6],其和为 1313

在第二个样例中,不需进行任何操作。

在第三个样例中,可以对子数组 [1,4][1,4] 执行操作,得到数组 [5,5,5,5][5, 5, 5, 5],其和为 2020

在第四个样例中,可以对子数组 [2,3][2,3] 执行操作,得到数组 [3,5,5,9,10][3, 5, 5, 9, 10],其和为 3232

由 ChatGPT 5 翻译

思路讲解

类似于求解最大字段和的 dp。

思路参考了 starsilk 的代码。

首先,我们必须要把 r,lr,l 两者分开。

image

我们就会发现,总答案就可以写为:(SxS_{x} 表示 xx 的前缀和)

ans=Sl1+l(1l)+SnSr+r(r+1)ans=S_{l-1}+l(1-l)+S_n-S_{r}+r(r+1)

这个明显可以使用这个 dp,因为我们只需要最优化 Sl1+l(1l)S_{l-1}+l(1-l) 这个值,就直接最优化了左端点。而右端点的转移,一旦不用考虑这个左端点是不是最优的,就容易多了。

那我们定义,dpi,0dp_{i,0} 为前缀和(操作前),dpi,1dp_{i,1} 表示从 ii 及之前开始转化的最大化 Sl1+l(1l)S_{l-1}+l(1-l) 值(可以理解为操作中,但是不严谨),dpi,2dp_{i,2} 表示 ii 以及之前转化结束,所能达到的最大和(操作后)。

之所以定义三个状态,而不是两个,是为了避免重复操作。

dpi,0=dpi1,0+aidpi,1=max(dpi1,1,dpi1,0+i(1i))dpi,2=max(dpi1,2+ai,dpi,1+i(i+1))dp_{i,0}=dp_{i-1,0}+a_i\\ dp_{i,1}=\max(dp_{i-1,1},dp_{i-1,0}+i(1-i))\\ dp_{i,2}=\max(dp_{i-1,2}+a_i,dp_{i,1}+i(i+1))

这个状态转移式子中 dpi,2dp_{i,2} 之所以有两个来源,就是其状态定义,在 ii 之前转化结束一个来源,在 ii 结束一个来源。

ii 定义为这个长整型,小心溢出

1
2
3
4
5
6
7
8
9
// dp[i][0] 就是前缀和,dp[i][1]是这个预备变量
for (ll i=1;i<=N;++i) {
dp[i][0]=dp[i-1][0]+A[i];
// dp[i][1] 代表从 i 开始变换,我们能得到的最大值(左端)
// 即 S[i-1]+i*(1-i) 的最大值
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+i*(1-i));
dp[i][2]=max(dp[i-1][2]+A[i],dp[i][1]+i*(i+1));
}
cout<<max(dp[N][0],dp[N][2])<<"\n";

AC代码

AC
https://codeforces.com/contest/2169/submission/360407408

心路历程(WA,TLE,MLE……)

题目大意

给定长度为nn的序列aa和正整数mmaa的每个元素都是范围在[0,m][0, m]内的整数。

如果且仅当以下两个条件满足时,序列aa被认为是好的

  • a1<a2<a3<<ana_1<a_2<a_3<\ldots<a_n,并且

  • 1lcm(a1,a2)+1lcm(a2,a3)++1lcm(an1,an)+1lcm(an,a1)1\frac{1} {\operatorname{lcm}(a_1,a_2)}+\frac{1} {\operatorname{lcm}(a_2,a_3)}+\ldots+\frac{1} {\operatorname{lcm}(a_{n-1},a_n)}+\color{red} {\frac{1} {\operatorname{lcm}(a_n,a_1)}}\ge1.^{\text{∗}}

你需要用范围在[1,m][1, m]内的整数替换aa中的所有零。计算替换零的不同方式的数量,使得得到的序列aa好的

998244353998\,244\,353取模后输出答案。

^{\text{∗}}两个正整数的最小公倍数(lcm\operatorname{lcm})是能同时整除它们的最小正整数。例如,lcm(2,3)=6,lcm(4,6)=12\operatorname{lcm}(2,3)=6, \operatorname{lcm}(4,6)=12

思路讲解

AC代码

心路历程(WA,TLE,MLE……)

题目大意

题目总结:Majority Wins?

1. 目标

给定一个长度为 nn 的二进制字符串 ss,通过最少总代价的操作将其最终转化为一个只包含字符 1 的字符串(即长度为 1 且内容为 1)。

2. 操作定义

在当前字符串中选择一段连续的子串 g[lr]g[l \dots r],计算其中 01 出现的次数:

  • 如果某个字符出现的次数 不少于 另一个字符(即出现次数 \ge 另一方),则可以用该字符替换掉整个子串。

  • 操作代价:等于该子串的长度,即 rl+1r - l + 1

  • 你可以进行多次操作,每次操作后的字符串长度会缩短。

例如,字符串010010可以通过用成本为44的操作将子串1001替换为1来在一次操作中转换为010;字符串1111可以通过在整个字符串上执行成本为44的操作转换为1;字符串0100可以通过取子串100转换为00。

3. 输出要求

  • 输出将字符串转化为 1 的最小总代价。

  • 如果无法转化,则输出 1


样例解释

  • 样例 1 (0):由于只有一个 0,无法通过任何操作产生 1。输出 1

  • 样例 2 (1):已经是 1,无需操作。输出 0

  • 样例 4 (10):选择子串 10(长度 2),其中 1 的数量(1个)不少于 0 的数量(1个),可替换为 1。代价为 2

  • 样例 5 (010)

    1. 选择子串 01 替换为 1,字符串变为 10,代价为 22
    2. 选择子串 10 替换为 1,代价为 22
    3. 总代价 2+2=42 + 2 = 4
  • 样例 6 (00111000)

    1. 选择子串 001111 占多数)替换为 1,字符串变为 1000,代价 55
    2. 选择子串 101 不少于 0)替换为 1,字符串变为 100,代价 22
    3. 再次对 10 进行操作变为 1,字符串变为 1,代价 22
    4. 总代价 5+2+2=95 + 2 + 2 = 9

思路讲解

其实这道题目就是一个分类讨论,感觉放在 C 也没什么问题。

  1. E was proposed as C, and C as B

不难发现,答案再大,不会超过 n+3n+3

image

然后直接输出 nn 的情况就是 1 的数量大于等于 0 的数量的时候。

输出 n+1n+1 是什么情况呢?首先 01 串首部或者尾部为 1 时,输出这个 n+1n+1

还有就是当存在一个位置,使得 suf(1)suf(0)+1\text{suf}(1)≥\text{suf}(0)+1 数量的时候(这个是因为多容纳一个 0 也没事)(suf(x)\text{suf}(x) 代表该位置后缀 xx 数量)。

同理可得 pre(1)pre(0)+1\text{pre}(1)≥\text{pre}(0)+1

那么什么时候输出这个 n+2n+2 呢?当存在一个位置,使得 suf(1)suf(0)\text{suf}(1)≥\text{suf}(0) 数量的时候(表示肯定有 0 嘛,那么我们不妨消掉一个,就可以回到上面一种情况)。

同理可得 pre(1)pre(0)\text{pre}(1)≥\text{pre}(0)

其他情况,就输出 n+3n+3

然后,你忐忑地提交了,发现 WA 了,为什么呢?对拍!

1
2
3
1
10
0000110000

该样例也应该输出这个 n+2n+2,但是我们输出了 n+3n+3,这个是因为什么呢?

image

我们发现 ‘11' 这个东西非常特殊,可以压制两边的 0,你会发现,‘101’ 就不可以压制两边的 0 了。

因此,只要找到 ‘11' 那么答案不会大于 n+2n+2

1
2
3
if (s.find("11")!=string::npos) {
ans=min(ans,N+2);
}

再次忐忑地提交,你发现竟然过了?

AC代码

AC

https://codeforces.com/contest/2189/submission/360277668

心路历程(WA,TLE,MLE……)