Codeforces 1834 / Codeforces Round #879 (Div. 2)

发布时间 2023-06-19 22:30:56作者: Meatherm

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;
}