Educational Codeforces Round 155 (Rated for Div. 2)C D

文章讨论了两个编程问题:如何在输入的01字符串中通过最少操作使其0和1交替出现,以及计算所有连续子数组异或值与长度之和。解决方案涉及分步乘法和位运算技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C. Make it Alternating(组合数学)

题目:输入一串01字符串,对其进行删除操作,使得0和1交替出现。求最小操作的次数,以及有多少种不同的操作。

思路:将连续的0/1段删除至只剩一个0/1。分步乘法,首先选择删除的数对于每个长度为len连续的段,有C(n, n-1)种删除选择,然后对于所有选择出来的删除的数求删除顺序,即进行全排列。

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
void solve()
{
	string s; cin >> s;
	int n = s.size();
	int ans = 0, ways = 1;
	for(int l = 0, r = 0; l < n; l = r)
	{
		while(s[l] == s[r] && r < n) r ++;
		ans = (ans + r - l - 1) % mod;
		ways = ways * (r - l) % mod;  
	}
	for(int i = 1; i <= ans; i ++) ways = ways * i % mod;
	cout << ans << " " << ways << '\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t = 1; cin >> t;
	while(t --) solve(); 
}

D. Sum of XOR Functions(位运算)

题目:求数组的所有连续子数组 异或值 乘以 长度 的累加。

思路:按位求贡献,对于每一位,要使区间的异或值有贡献,必须满足区间内所有数异或值为1。

先预处理出前缀异或,那么对于每一位,我们可以顺序枚举右端点i = r,若右端点的前缀异或为1,则寻找之前所有位置为0的点l,这些l+1 到 r的长度之和即为贡献。若右端点的前缀异或为0同理。

这些l+1 到 r的长度之和可以用一个值s来维护,以该位右端点 r 前缀异或值为1为例,每往下走一位,对于每一个左端点值为0 的 a[l+1 ~ r]区间,它们的距离都比a[l+1 ~ r - 1]多1,共有cnt0个这样的区间,故s += cnt0.

参考题解:

​​​​​​Educational Codeforces Round 155 (Rated for Div. 2)(A~D) - 知乎 (zhihu.com)

#include<bits/stdc++.h>
#define double long double
#define int long long
using namespace std;
const int N = 2e5 + 10, mod = 998244353;
void solve()
{
	int n; cin >> n;
	vector<int>a(n + 1), s(n + 1); 
	int x, ans = 0;
	for(int i = 1; i <= n; i ++) cin >> x, s[i] = s[i - 1] ^ x;
	for(int k = 0; k <= 30; k ++)
	{
		int cnt0 = 1, cnt1 = 0, s0 = 1, s1 = 0;
		for(int i = 1; i <= n; i ++)
		{
			int tmp = (s[i] >> k) & 1;
			if(tmp)
			{
				ans = (ans + s0 * (1 << k) % mod) % mod;
				cnt1 ++;
			}
			else
			{
				ans = (ans + s1 * (1 << k) % mod) % mod;
				cnt0 ++;
			}
			s0 = (s0 + cnt0) % mod;
			s1 = (s1 + cnt1) % mod; 
		}
	}
	cout << ans << '\n';
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t = 1; //cin >> t;
	while(t --) solve(); 
}
/*
3
1 0 1
1 + 1 + 4(1 + 3
a 1 0 1
s 1 1 0
*/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值