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
*/