Codeforces Round #828 (Div. 3)练习笔记

本文介绍了五道算法竞赛题目,包括数的替换、奇偶增量、交通信号灯、因数与除数操作以及MEX与MED的比较。每道题目提供了详细的解题思路和C语言实现代码,涉及数组操作、字符串处理、数学计算和区间分析等算法知识。
摘要由CSDN通过智能技术生成

\(\text{A.Number Replacement}\)

给定长度为 \(n(n\leq 50)\) 的整数数组 \(a(a_i\leq 50)\) 和一个长度为 \(n\) 的字符串 \(s\)
定义一次操作为:选定整数 \(x\) 和字符 \(c\) ,将数组所有等于 \(x\) 的位置变成 \(c\) 。问是否能通过多次操作将 \(a\) 变为 \(s\)\(T(T\leq 1000)\) 组数据。\(\text{sol.}\) 注意到 \(a_i\) 很小,所以可以从左往右扫一遍,记录 \(id_{a_i}=s_i\) 。若当前 \(id_{a_i}\) 已经有值了且值不等于 \(s_i\) ,则无法将 \(a\) 变为 \(s\)

cint maxn = 60;
int T, n, a[maxn], id[maxn], fl;
char s[maxn];
int main(){
	T = rd();
	while(T--){
		n = rd(), fl = 0;
		fp(i, 1, n)a[i] = rd(), id[a[i]] = -1;
		scanf("%s", s+1);
		fp(i, 1, n){
			if(~id[a[i]] && id[a[i]] != s[i]-'a'){ fl = 1; break; }
			id[a[i]] = s[i]-'a';
		}
		puts(fl ? "NO" : "YES");
	}
	return 0;
}

\(\text{B. Even-Odd Increments}\)

给定长度为 \(n(n\leq 10^5)\) 的数组 \(a(a_i\leq 10^9)\) ,接下来有 \(q(q\leq 10^5)\) 次操作,每次操作将数组里的所有奇数或者所有偶数都加上某个值 \(([1,10^4])\) 。要求在每次操作后输出整个数组的和。多组数据。
直接记录一下现在数组里有多少个奇数、多少个偶数,每次操作的时候答案的改变值就可以直接计算出来。如果加的数是奇数,则需要修改一下奇数和偶数的个数。

cint maxn = 100010;
int T, n, q, a[maxn], cnt[2];
LL ans;
int main(){
	T = rd();
	while(T--){
		n = rd(), q = rd(), ans = cnt[0] = cnt[1] = 0;
		fp(i, 1, n)a[i] = rd(), ++cnt[a[i]&1], ans += a[i];
		while(q--){
			rg int typ = rd(), x = rd();
			ans += 1ll*cnt[typ]*x;
			if(x&1)cnt[!typ] += cnt[typ], cnt[typ] = 0;
			printf("%lld\n", ans);
		}
	}
	return 0;
}

\(\text{C. Traffic Light}\)

给定一个长度为 \(n(n\leq 2\times 10^5)\) 的只包含 \(\text{r,y,g}\)环形字符串,再给定一个 \(\text{r,y,g}\) 中的字符 \(c\) 。定义 \(f_i\) 表示第 \(i\) 个字符后面最近的 \(\text g\)\(i\) 的距离。对于所有字符 \(c\) ,求出 \(f\) 的最大值。多组数据。
按照翻译后的题意做就行。处理环形字符串可以直接复制一遍接在后面。

cint maxn = 200010;
int T, n, nxt[maxn<<1], pos, ans;
char now, s[maxn<<1];
int main(){
	scanf("%d", &T);
	while(T--){
		scanf("%d %c", &n, &now), scanf("%s", s+1), pos = n<<1|1, ans = 0;
		fp(i, 1, n)s[n+i] = s[i];
		fb(i, n<<1, 1){
			if(s[i] == 'g')pos = i;
			nxt[i] = pos;
		}
		fp(i, 1, n)if(s[i] == now && nxt[i]-i > ans)ans = nxt[i]-i;
		printf("%d\n", ans);
	}
	return 0;
}

\(\text{D. Divisibility by 2^n}\)

给定一个长度为 \(n(n\leq 2\times 10^5)\) 的数组 \(a(a_i\leq 10^9)\) 。定义一次操作为将某个 \(a_i\) 变成 \(a_i\times i\) 。每个 \(i\) 只能被操作 \(1\) 次。求最少需要多少次操作,使得 \(2^n | \prod_{i=1}^n a_i\) 。多组数据。
首先求出 \(\prod_{i=1}^n a_i\) 中有多少个 \(2\) 相乘,假设有 \(m\) 个。然后将 \(1\)~\(n\) 中每个数的 \(2\) 次幂分离出来,并从大到小排序。按照 \(2\) 次幂的大小从大到小依次加入每个数的 \(2\) 次幂,一旦加起来大于等于 \(n-m\) 次就停止。此时加了多少次就是最少的操作次数。

cint maxn = 200010;
int T, n, a[maxn], f[maxn], sum, cnt;
int main(){
	T = rd();
	while(T--){
		n = rd(), sum = cnt = 0;
		fp(i, 1, n){
			a[i] = rd();
			rg int x = a[i];
			while(x && x%2 == 0)x >>= 1, ++sum;
		}
		fp(i, 1, n){
			f[i] = 0;
			rg int x = i;
			while(x && x%2 == 0)x >>= 1, ++f[i];
		}
		sort(f+1, f+1+n), reverse(f+1, f+1+n);
		while(cnt < n && sum < n)sum += f[++cnt];
		if(sum < n)puts("-1");
		else printf("%d\n", cnt);
	}
	return 0;
}

\(\text{E. Divisible Numbers}\)

给定 \(4\) 个数 \(a,b,c,d(1\leq a<c\leq 10^9,1\leq b<d\leq 10^9)\) 。求出 \(x,y\) ,满足 \(a<x\leq c,b<y\leq d\) 并且 \(ab|xy\)\(T(T\leq 10)\) 组数据。\(x,y\) 都可以写成 \(a\) 的因数 \(\times\) \(b\) 的因数 \(\times\) 一个系数的形式。注意到一个数的因数个数很少,所以可以枚举一个 \(a\) 的因数 \(f_1\) ,再枚举一个 \(b\) 的因数 \(f_2\) ,那么 \(x=k_1f_1f_2,y=k_2\times \frac{ab}{f_1f_2}\) 。只需要求出 \(k_1,k2\) 使得 \(x,y\) 在题目要求的范围即可。

cint maxn = 100010;
int T, a, b, c, d, f[2][maxn], cnt[2], fl;
int main(){
	T = rd();
	while(T--){
		a = rd(), b = rd(), c = rd(), d = rd(), fl = cnt[0] = cnt[1] = 0;
		for(rg int i = 1; i*i <= a; ++i)if(a%i == 0){
			f[0][++cnt[0]] = i;
			if(i*i != a)f[0][++cnt[0]] = a/i;
		}
		for(rg int i = 1; i*i <= b; ++i)if(b%i == 0){
			f[1][++cnt[1]] = i;
			if(i*i != b)f[1][++cnt[1]] = b/i;
		}
		fp(i, 1, cnt[0])if(!fl)fp(j, 1, cnt[1]){
			rg LL x = 1ll*f[0][i]*f[1][j];
			rg LL y = 1ll*a*b/x;
			x = a/x*x+x, y = b/y*y+y;
			if(a < x && x <= c && b < y && y <= d){ printf("%lld %lld\n", x, y), fl = 1; break; }
		}
		if(!fl)puts("-1 -1");
	}
	return 0;
}

\(\text{F. MEX vs MED}\)

给定一个长度为 \(n(n\leq 2\times 10^5)\) 的由 \(0\)~\(n-1\) 的排列 \(p\)
定义 \(\text{mex}\) 为数列中最小的未出现的非负整数。
定义 \(\text{med}\) 为数列的中位数(若长度为偶数,则取第 \(\frac{len}{2}\) 小的数)。
求出有多少个区间满足 \(\text{mex} > \text{med}\) 。多组数据。
假设一个区间的 \(\text{mex}=m\) ,那么这个区间中就得出现 \([0,m-1]\) 中所有的数。由此可以得出,区间长度不能超过 \(2m\) ,否则 \(\text{med}\) 将大于 \(m\)
所以我们可以枚举 \(m\) ,维护最小的包含了 \([0,m-1]\) 所有数的区间。接下来考虑拓展这个区间。必然是某一个方向拓展到 \(m\) 所在位置,另一个方向拓展到数组尽头,所以直接计算有多少个满足条件的区间即可。
计算时可以直接枚举 \(m\) 所在的那一边的长度,这样可以保证复杂度是线性的。

cint maxn = 200010;
int T, n, p[maxn], pos[maxn], l, r, mex, rst;
LL ans;
int main(){
	T = rd();
	while(T--){
		n = rd(), ans = 0;
		fp(i, 1, n)p[i] = rd(), pos[p[i]] = i;
		l = n+1, r = 0, mex = 0;
		fp(i, 0, n-1){
			if(l <= pos[i] && pos[i] <= r)continue;
			if(pos[i] < l)l = pos[i];
			if(pos[i] > r)r = pos[i];
			while(mex < n && l <= pos[mex] && pos[mex] <= r)++mex;
			if(mex == n)break;
			rst = mex*2-(r-l+1);
			if(pos[mex] < l)fp(i, pos[mex]+1, l)ans += max(0, min(n-r+1, rst-(l-i)+1));
			else fp(i, r, pos[mex]-1)ans += max(0, min(l, rst-(i-r)+1));
		}
		printf("%lld\n", ans+1);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值