2025/7/27 模拟赛总结
\(0+0+0+0=0\) 没考
赛时记录:
???
显然 \(f(x, y)\) 一定只会等于 \(1\) 或 \(2\),而且只会在 \(x \ \text{and}\ y=0\) 时答案为 \(1\)。于是问题变成了求有多少对数的按位与不为零
由于 \(\text{popcount}\le 4\),考虑容斥。用 map 存下所有数的二进制位组合,直接容斥即可
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 2e5 + 5, kM = 32;
LL T = 1, n, a[kMaxN], ans, c[kM], tot, e[kM];
map<LL, LL> mp1;
map<LL, map<LL, LL>> mp2;
map<LL, map<LL, map<LL, LL>>> mp3;
map<LL, map<LL, map<LL, map<LL, LL>>>> mp4;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
for (cin >> T; T; T--, mp1.clear(), mp2.clear(), mp3.clear(), mp4.clear(), ans = 0) {
cin >> n, ans = n * (n - 1) / 2;
for (int i = 1, tmp; i <= n; i++) {
cin >> a[i], c[0] = 0;
for (int j = 0; j < 30; j++) {
(a[i] >> j & 1) && (c[++c[0]] = j);
}
for (int j = 1; j < 1 << c[0]; j++) {
tot = 0;
for (int k = 0; k < c[0]; k++) {
j >> k & 1 && (e[++tot] = k);
}
if (tot == 1) {
mp1[c[e[1] + 1]]++;
} else if (tot == 2) {
mp2[c[e[1] + 1]][c[e[2] + 1]]++;
} else if (tot == 3) {
mp3[c[e[1] + 1]][c[e[2] + 1]][c[e[3] + 1]]++;
} else {
mp4[c[e[1] + 1]][c[e[2] + 1]][c[e[3] + 1]][c[e[4] + 1]]++;
}
}
}
for (auto i : mp1) {
ans += i.second * (i.second - 1) / 2;
}
for (auto i : mp2) {
for (auto j : i.second) {
ans -= j.second * (j.second - 1) / 2;
}
}
for (auto i : mp3) {
for (auto j : i.second) {
for (auto k : j.second) {
ans += k.second * (k.second - 1) / 2;
}
}
}
for (auto i : mp4) {
for (auto j : i.second) {
for (auto k : j.second) {
for (auto l : k.second) {
ans -= l.second * (l.second - 1) / 2;
}
}
}
}
cout << ans << '\n';
}
return 0;
}
首先长度大于等于 \(k\) 的连续段超过 \(2\) 个或者某一个先续断长度超过了 \(2k-2\) 一定无解
考虑连续段有 \(2\) 个的情况,如果两个连续段的字符相同则还是无解,否则贪心的旋转一定有解,反正不需要输出具体方案
再考虑 \(1\) 个连续段的情况。不妨设这个连续段子字符为 A,反之亦然。将第 \(0\) 位和第 \(n+1\) 位设成 B,如果有两个连续的 B 则在它们之间旋转,一定有解。否则如果一段连续的 A 接上转出来的一段 A 后长度仍小于 \(k\) 则有解。否则无解
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 2e5 + 5;
int T, n, k, ans = 1, cnt = 1, l[2], r[2], mx;
string s;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
for (cin >> T; T; T--, cnt = ans = 1, l[0] = l[1] = r[0] = r[1] = 0) {
cin >> n >> k >> s, s = ' ' + s;
for (int i = 2; i <= n; i++) {
if (s[i] == s[i - 1]) {
cnt++;
} else {
if (cnt >= k) {
if (l[1]) {
ans = 0;
break;
}
l[0] ? (l[1] = i - cnt, r[1] = i - 1) : (l[0] = i - cnt, r[0] = i - 1);
}
cnt = 1;
}
}
if (cnt >= k) {
if (l[1]) {
ans = 0;
}
l[0] ? (l[1] = n - cnt + 1, r[1] = n) : (l[0] = n - cnt + 1, r[0] = n);
}
if (ans == 0) {
cout << "NO\n";
continue;
}
if (!l[0]) {
cout << "YES\n";
continue;
}
if (!l[1]) {
if (r[0] - l[0] + 1 >= 2 * k - 1 || l[0] == 1 && r[0] == n) {
cout << "NO\n";
continue;
}
ans = 0;
for (int i = 2; i <= n; i++) {
if (s[l[0]] != s[i] && s[i] == s[i - 1]) {
ans = 1;
break;
}
}
if (ans) {
cout << "YES\n";
continue;
}
mx = 1e9, cnt = 1;
for (int i = 1; i <= n; i++) {
if (s[i] == s[i - 1]) {
cnt++;
} else {
if (s[i - 1] == s[l[0]] && i - 1 != r[0]) {
mx = min(mx, cnt);
}
cnt = 1;
}
}
if (s[n] == s[l[0]] && n != r[0]) {
mx = min(mx, cnt);
}
cout << ((mx == 1e9 || s[n] != s[l[0]] || s[1] != s[l[0]] || mx + r[0] - l[0] + 1 - k + 1 < k) ? "YES" : "NO") << '\n';
} else {
if (r[0] - l[0] + 1 >= 2 * k - 1 || r[1] - l[1] + 1 >= 2 * k - 1) {
cout << "NO\n";
continue;
}
cout << (s[l[0]] != s[l[1]] ? "YES" : "NO") << '\n';
}
}
return 0;
}
最小值最大想到二分答案,又因为 \(k\le 17\) 的提示想到状压。令 \(dp_{s}\) 为填入了集合 \(s\) 中的字母后用到的最短前缀长度,每次 Check 时预处理出 \(f_{i,j}\) 为在尽量填入 \(j\) 的情况下,在 \(i\) 之后最靠前的长度为 \(mid\) 的均为 \(j\) 的连续段。转移式子直接根据 \(f\) 取 \(\min\) 即可
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 2e5 + 5, kMaxK = 18, kL = 1 << kMaxK;
int n, k, L, R, f[kMaxN][kMaxK], dp[kL];
string s;
bool Chk(int x) {
for (int c = 1; c <= k; c++) {
f[n + 1][c] = INT_MAX;
for (int i = n, p = 0; i; i--) {
p += (s[i] - 'a' + 1 == c || s[i] == '?') ? 1 : -p;
f[i][c] = p >= x ? i + x - 1 : f[i + 1][c];
}
}
fill(dp, dp + kL, INT_MAX), dp[0] = 0;
for (int i = 0; i < 1 << k; i++) {
for (int j = 1; j <= k; j++) {
if (i >> (j - 1) & 1 ^ 1 && dp[i] + 1 <= n) {
dp[i + (1 << j - 1)] = min(dp[i + (1 << j - 1)], f[dp[i] + 1][j]);
}
}
}
return dp[(1 << k) - 1] <= n;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> k >> s, s = ' ' + s, L = 0, R = n;
for (int mid = L + R + 1 >> 1; L < R; mid = L + R + 1 >> 1) {
Chk(mid) ? L = mid : R = mid - 1;
}
cout << L << '\n';
return 0;
}
一次送餐的操作相当于对于 \([l+1,r]\) 每一次令 \(cur:=\max(cur+t_i,l_{i+1})\)。这个式子纯动态 dp,矩阵类型为 \(\max+\) 矩阵,还需要存下 \(\max\{cur-r_i\}\),这样输出时就可以快速判断了
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 1e6 + 5;
LL T, n, l[kMaxN], r[kMaxN], t[kMaxN], q, op, p, x, y;
struct M {
LL n, m, c[3][3];
M() { memset(c, 0xc0, sizeof(c)); }
M(int n_, int m_) { n = n_, m = m_, memset(c, 0xc0, sizeof(c)); }
LL* operator[](int p) { return c[p]; }
M operator*(M o) {
M ret(n, o.m);
for (int i = 0; i < ret.n; i++) {
for (int k = 0; k < m; k++) {
for (int j = 0; j < ret.m; j++) {
ret[i][j] = max(ret[i][j], c[i][k] + o[k][j]);
}
}
}
return ret;
}
} ans(1, 3), tr[kMaxN << 2], G;
M Calc(int p) {
M ret(3, 3);
return ret[0][0] = t[p], ret[0][2] = -r[p], ret[1][0] = l[p + 1], ret[1][1] = ret[2][2] = 0, ret;
}
void pushup(int x) { tr[x] = !tr[x << 1].n ? tr[x << 1 | 1] : (!tr[x << 1 | 1].n ? tr[x << 1] : tr[x << 1] * tr[x << 1 | 1]); }
void build(int x, int l, int r) {
if (l == r) {
return tr[x] = Calc(l), void();
}
int mid = l + r >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r), pushup(x);
}
void update(int x, int l, int r, int p) {
if (l == r) {
return tr[x] = Calc(p), void();
}
int mid = l + r >> 1;
p <= mid ? update(x << 1, l, mid, p) : update(x << 1 | 1, mid + 1, r, p);
pushup(x);
}
M query(int x, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return tr[x];
}
int mid = l + r >> 1;
if (R <= mid) {
return query(x << 1, l, mid, L, R);
} else if (L > mid) {
return query(x << 1 | 1, mid + 1, r, L, R);
} else {
return query(x << 1, l, mid, L, R) * query(x << 1 | 1, mid + 1, r, L, R);
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
for (cin >> T; T; T--) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> l[i];
}
for (int i = 1; i <= n; i++) {
cin >> r[i];
}
for (int i = 1; i < n; i++) {
cin >> t[i];
}
build(1, 1, n);
for (cin >> q; q; q--) {
cin >> op >> p >> x;
if (op == 0) {
ans[0][0] = l[p], ans[0][1] = 0, ans[0][2] = -4e18;
G = query(1, 1, n, p, x), ans = ans * G;
cout << (ans[0][2] <= 0 ? "Yes" : "No") << '\n';
} else if (op == 1) {
t[p] = x, update(1, 1, n, p);
} else {
cin >> y, l[p] = x, r[p] = y;
p != 1 && (update(1, 1, n, p - 1), 0), update(1, 1, n, p);
}
}
}
return 0;
}

浙公网安备 33010602011771号