Codeforces 1834 / Codeforces Round #879 (Div. 2)
Contest Link
Codeforces Round #879 (Div. 2)
Problem B Maximum Strength
题意
给定 \(L,R\),求两个整数 \(L \leq a,b \leq R\) 使得 \(a,b\) 的数位差之和最大。例如 \(133\) 和 \(219\) 的数位差是 \(|1-2|+|3-1|+|3-9| = 9\),位数不足用前导零补齐。
\(1 \leq L \leq R < 10^{100}\)
题解
考场上脑子瓦特了,想的 DP:钦定 \(a \leq b\)。设 \(f(i,0/1,0/1,0/1)\) 分别表示高 \(i\) 位 \(a\) 是否一直处于下界,\(b\) 是否处于上界,是否已经选取的位有 \(a=b\),然后枚举当前位选的数,判断是否合法。
但事实上我们发现答案最大就是找到最高的位使得 \(L,R\) 的这一位 \(l_i,r_i\) 不同,然后 \(a\) 的这一位选 \(r_i\),\(b\) 的这一位选 \(l_i\),之后 \(a\) 一直选 \(0\),\(b\) 一直选 \(9\)。
例如:\(L = 558244, R = 559244\),取 \(a=559000,b=558999\) 最优。
Problem C Game with Reversing
题意
Alice Bob 玩游戏。现在有两个长为 \(n\) 的小写字母串 \(S,T\),从 Alice 开始轮流操作,Alice 可以选择串里的某个位置,改成另一个小写字母(可以和原来相同),Bob 可以选一个串进行翻转。
Alice 想要游戏尽快结束,Bob 想要游戏尽可能长。求游戏结束时总共操作了多少次。
\(1 \leq n \leq 10^5\)
题解
我们发现 Bob 的具体操作策略对答案没有影响,因为翻两次 \(S\) 和翻一次 \(S\),翻一次 \(T\) 本质相同。我们只需要关心游戏结束时,Bob 究竟操作了多少次。如果操作了奇数次,Alice 必须把 \(S\) 改的和 \(T\) 的反串相同,否则必须把 \(S\) 和 \(T\) 改的相同。
我们进一步发现,Alice 的操作次数只和 \(S\) 和 \(T\)(或其反串的差异)有关。于是分类讨论 Bob 操作次数的奇偶性,然后计算 Alice 的操作次数即可。注意有可能在 Bob 操作偶数次的情况下,Alice 需要操作的次数是一个奇数(比如 \(5\)),这个时候 Alice 必须要放空一轮,等 Bob 的第 \(6\) 次操作。同理另一侧也有一些特殊情况,处理一下即可。
Problem D Survey in Class
题意
现在有 \(n\) 个学生,\(m\) 个学科,第 \(i\) 个学生只会 \([l_i,r_i]\) 的学科的问题。现在你作为老师,可以问一些学科的问题,当然,每一科最多问一次。初始每个学生的手高度为 \(0\)。如果学生会该问题,则手的高度增加 \(1\),否则减少 \(1\)。问在所有可能的问法中,最后学生中最高的手和最低的手的高度差最大是多少。
\(2 \leq n \leq 10^5,1 \leq l_i \leq r_i \leq m \leq 10^9\)
题解
考虑钦定两个学生是最高的和最低的。发现问他们都会的和都不会的都没有意义。只需要问一个人会的,另一个人不会的,才会拉开差距。设两个人会的集合分别是 \(a,b\),那么要求的就是最大的 \(|a|-|a \cap b|\)。
考虑 \(b\) 怎么交 \(a\):要么和 \(a\) 的左端点交,要么和 \(a\) 的右端点交,要么完全在 \(a\) 内部。于是,我们只需要检查三条线段:右端点最小的,左端点最大的,长度最小的。如果其中某些线段和 \(a\) 没有交,那无疑是更优的。
有瓜皮考场上写了线段树。😃
Problem E MEX of LCM
题意
给定长度为 \(n\) 的正整数数组 \(a\),问最小的正整数 \(x\) 使得不存在任何区间的 LCM 为 \(x\)。
\(1 \leq n \leq 3 \times 10^5, 1 \leq a_i \leq 10^9\)
题解
注意到 \(n^2\) 个数的 MEX 不超过 \(n^2+1\),于是只有 LCM 小于等于 \(n^2 + 1\) 的才可能成为答案。这也说明,同一右端点的所有区间,至多只有不同的 \(2 \log_2 n + 1\) 个 LCM 可能成为答案:设这些 LCM 分别为 \(x_1 < x_2 < \cdots < x_k\),显然有 \(x_{i+1}\) 被 \(x_i\) 整除,从而 \(x_{i+1} \geq 2x_i\)。
到这里已经可以暴力向右移动端点,然后做完了。
同时,上面的方法也证明了答案甚至是不超过 \(10^9\) 的,于是我们不用担心溢出。
Problem F Typewriter
题意
给定一个长度为 \(n\) 的排列 \(p\),初始有一个指向 \(1\) 的指针和一个空的缓冲区。
你可以执行以下操作:
- 如果当前指针指向的位置非空且缓冲区是空的,把这个位置上的数扔进缓冲区
- 如果缓冲区非空且当前指针指向的位置是空的,把缓冲区中的数扔到这个位置
- 如果它们都非空,交换这两个数
- 指针右移 \(1\)
- 指针移回 \(1\)
现在有 \(q\) 次操作,形如:
- 排列向左循环移位 \(k\) 步
- 排列向右循环移位 \(k\) 步
- 翻转排列
在第一次操作前和每次操作后,询问使用上述操作将当前排列变为 \(1,2,\cdots,n\) 的最小指针移回 \(1\) 的次数。注意到这些操作不会真实发生。
\(1 \leq n \leq 4 \times 10^5\)
题解
指针只会向右移动,显然每一次指针移回 \(1\) 只能让一个 \(p_i < i\) 的位置归位,于是答案的下界就是满足条件的 \(i\) 数量。同时,我们可以构造出这个下界:每次把 \(p_i\) 位置的数拿起,到 \(i\) 位置执行交换,指针移回 \(1\),最后到 \(p_i\) 位置放下。为了操作冲突最小,我们按照 \(p_i\) 从小到大对于所有符合条件的 \(i\) 操作即可。
现在问题就是怎么维护 \(p_i < i\) 的位置数量。首先观察到左移和右移没有本质区别,我们只需要维护数组的开头是哪些位置的时候,\(p_i < i\) 成立。不难发现这是一个连续段(也有可能是前缀 + 后缀),差分即可。
对于翻转,我们直接对于翻转后的 \(p\) 也维护上面的即可。考虑翻转后,左移和右移的意义恰好与翻转前相反,维护即可。
# include <bits/stdc++.h>
const int N=400010,INF=0x3f3f3f3f;
int n,q;
int T;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
int p[N];
struct Node{
int a[N],ans[N];
inline void init(bool op){
for(int i=1;i<=n;++i) a[i]=p[op?(n-i+1):i],ans[i]=0;
for(int i=1;i<=n;++i){ // ai < i
if(a[i]<=i) ++ans[1],--ans[i-a[i]+1],++ans[i+1],--ans[n+1];
// [1,i-ai] and [i+1,n] when ai<=i; [i+1,n-(ai-i)] when ai>i;
else ++ans[i+1],--ans[n-(a[i]-i)+1];
}
for(int i=1;i<=n;++i) ans[i]+=ans[i-1];
return;
}
}sim,rev;
int main(void){
T=1;
while(T--){
n=read();
for(int i=1;i<=n;++i) p[i]=read();
q=read();
sim.init(false),rev.init(true);
int sh=0,re=1;
printf("%d\n",sim.ans[1]);
while(q--){
int op=read(); // + : left shift - : right shift
if(op==1) sh+=read()*re;
else if(op==2) sh-=read()*re;
else re*=-1;
sh=(sh%n+n)%n;
if(re==1) printf("%d\n",sim.ans[sh+1]);
else printf("%d\n",rev.ans[(n-sh)%n+1]); // reversed
}
}
return 0;
}