A
直接统计 个骰子的结果为
的个数与
比大小即可。
#include <bits/stdc++.h>
int main() {
int t;
std::cin >> t;
while (t--) {
std::vector<int> a(12);
for (int i = 0; i < 12; i++) {
std::cin >> a[i];
}
int x, y;
std::cin >> x >> y;
int cnt = 0;
for (int i = 0; i < 12; i++) {
if (a[i] == y) {
cnt++;
}
}
std::cout << (cnt < x ? "win" : "lose") << '\n';
}
return 0;
}
B
注意到 ,以及
只有两种
两种取值,记
为序列中
的个数,
为序列中
的个数。
只可能出现三种情况:
:
可选的个数超过
的一半。
:
可选的个数足够
个。
:否则中位数还是
,然后
也没法不选。
#include <bits/stdc++.h>
int main() {
int t;
std::cin >> t;
while (t--) {
int n, k;
std::cin >> n >> k;
int one = 0;
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
if (x == 1) {
one += 1;
}
}
if ((n - one >= k) || one > k / 2) {
std::cout << 0 << '\n';
} else {
std::cout << 1 << '\n';
}
}
return 0;
}
C
记初始序列中正数个数为 ,负数个数为
,零的个数为
。
注意两个地方:
只能消除正数但不能增加正数。
- 负数可以增加正数也可以消除正数,
个负数最多变成
个正数。
#include <bits/stdc++.h>
int main() {
int t;
std::cin >> t;
while (t--) {
int n, m;
std::cin >> n >> m;
int pos = 0, neg = 0, zero = 0;
for (int i = 0; i < n; i++) {
int x;
std::cin >> x;
if (x > 0) {
pos += 1;
} else if (x < 0) {
neg += 1;
} else {
zero += 1;
}
}
int ans = std::abs(pos - m);
if ((pos > m && !zero && !neg) || (pos < m && pos + neg - 1 < m)) {
ans = -1;
}
std::cout << ans << '\n';
}
return 0;
}
D
- 对于任意一个从
的操作序列来说,一定可以通过调整操作
的顺序先使用操作
后使用操作
,使得操作次数不变,且仍然可以让
。
- 在最优操作中,
操作至多使用一次。
证明:
用
表示操作
,
表示操作
。
考虑这样的一个操作序列:
,其中
分别表示前缀操作序列和后缀操作序列。
令
做完前缀操作序列
之后的值为
,操作
选的那个数为
,
,
。
我们交换两个操作的顺序,先做操作
再做操作
,令
。
我么只要证明存在一个
,使得
即可,不难注意到
即可。
所以我们证明了对于任意一个相邻的操作,如果
在
的前面,一定可以交换顺序,且交换之后操作完得到的数还是一样的,同时后缀
的操作序列没变,所以最后得到的结果还是
,且操作总次数没变多。
故任意一种可行的序列一定可以通过交换顺序变成
。
然后我们在注意到连续的操作
是没意义的,因为只要令
,那么这一段连续的操作
可以被合并为一段。
综上最优操作序列一定只有两种情况:
或者
。
所以可以枚举操作 的操作次数,然后判断是否还需要一次操作
去让
。
时间复杂度 。
#include <bits/stdc++.h>
int main() {
int t;
std::cin >> t;
while (t--) {
long long x, y;
std::cin >> x >> y;
int ans = 0;
while ((x & y) != x) {
ans++;
x >>= 1;
}
std::cout << ans + (x != y) << '\n';
}
return 0;
}
E
做法一:预处理前后缀
子序列的匹配是贪心的,对于 来说,他一定是在前缀
从左到右找某些项匹配,在后缀
从右往左找某些项拼接起来,所以考虑计算两个数组
与
。
表示满足
是
的子序列的
的最大值。
表示满足
是
的子序列的
的最大值。
答案即为 。
显然处理 与计算答案复杂度都是
的。
#include <bits/stdc++.h>
int main() {
int n;
std::cin >> n;
std::vector<int> P(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> P[i];
}
std::vector<int> lsz(n + 1), rsz(n + 1);
for (int i = 2; i < n; i++) {
int jl = 1, jr = n;
while (jl < i && i + lsz[i] <= n) {
if (P[jl] == P[i + lsz[i]]) {
lsz[i]++;
}
jl++;
}
while (jr > i && i - rsz[i] >= 1) {
if (P[jr] == P[i - rsz[i]]) {
rsz[i]++;
}
jr--;
}
}
int ans = 0;
for (int i = 2; i < n; i++) {
for (int j = i; j < n; j++) {
ans += lsz[i] + rsz[j] >= j - i + 1;
}
}
std::cout << ans << '\n';
return 0;
}
做法二:二分
首先要注意到:
对于 ,若
是
的子序列,那么一个更小的区间
,也必然满足
是
的子序列。
基于这个性质,我么可以枚举 ,通过二分出最大的一个
满足
是
的子序列,那么对于
都是满足条件的。
时间复杂度
#include <bits/stdc++.h>
int main() {
int n;
std::cin >> n;
std::vector<int> P(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> P[i];
}
int ans = 0;
auto check = [&](int l, int r) {
int idx = l;
for (int i = 1; i <= n; i++) {
if (i == l) {
i = r;
continue;
}
if (idx <= r && P[idx] == P[i]) {
idx++;
}
}
return idx == r + 1;
};
for (int i = 2; i < n; i++) {
int l = i - 1, r = n - 1;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(i, mid)) {
l = mid;
} else {
r = mid - 1;
}
}
ans += l - i + 1;
}
std::cout << ans << '\n';
return 0;
}
做法三:双指针
同样注意到上面的那个性质,不难发现对于 如果存在满足条件的
,那么
随着
的增加是单调不降的,所以可以用双指针。
时间复杂度 。
#include <bits/stdc++.h>
int main() {
int n;
std::cin >> n;
std::vector<int> P(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> P[i];
}
int ans = 0;
auto check = [&](int l, int r) {
int idx = l;
for (int i = 1; i <= n; i++) {
if (i == l) {
i = r;
continue;
}
if (idx <= r && P[idx] == P[i]) {
idx++;
}
}
return idx == r + 1;
};
for (int i = 2, j = 2; i < n; i++) {
j = std::max(j, i);
while (j < n && check(i, j)) {
j++;
}
ans += j - i;
}
std::cout << ans << '\n';
return 0;
}
F
先考虑暴力 DP 怎么做。
令 表示考虑了前
个位置,若
为奇数,则
表示第
个位置
的值;若
为偶数,则
表示
的值的方案数。
枚举 与当前位置
的取值
去转移:
- 若
为奇数,若满足
,则
。
- 若
为偶数,则
。
这样的 DP 复杂度是 的,显然无法通过。
考虑能否优化掉转移枚举 。
首先 为偶数的情况是好优化的:
- 若
,那么对于
来说,
形成的是一个连续的区间
。
- 若
,那么对于
来说,
可以形成两个连续的区间:
与
。
那么我么需要对这些区间的 加上
,这个过程显然可以通过差分优化到
。
对于 为奇数的情况:
首先需要先注意到对于给定的 ,所有满足
的
至多形成
个互不重叠的连续区间。
考虑前缀和思想,我么可以分两部做:
- 对满足
的所有
都加上
。
- 对满足
的所有
都减去
。
所以,我们可以先求出对于固定的 ,满足
的
形成的
个互不重叠的连续区间。(这个性质可以从经典的
字典树去想,考虑从二进制高位到低位去确定可能的
的范围)
这个过程可以用 的复杂度算出来,那么再结合
的限制
,我么可以得到最终满足条件的
个区间:
。
此时需要对这些区间的 加上
,这个过程也可以用差分优化,同理处理
也一样。
综上对于 为奇偶的情况都可以用差分优化,所以 DP 的时间复杂度可以降为
。
最后答案即为 ,故总的时间复杂度
。
#include <bits/stdc++.h>
constexpr int mod = 1e9 + 7;
constexpr int MAX = 1 << 10;
constexpr int MAX_BIT = 10;
void Add(int& x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
if (x < 0) {
x += mod;
}
}
std::vector<std::array<int, 2>> GetSegXOR(int x, int y) {
std::vector<std::array<int, 2>> seg;
if (y < 0) {
return seg;
}
int p = 0;
for (int i = MAX_BIT - 1; i >= 0; i--) {
if (y >> i & 1) {
if (x >> i & 1) {
seg.push_back({p | (1 << i), p | ((1 << (i + 1)) - 1)});
} else {
seg.push_back({p, p | ((1 << i) - 1)});
p |= 1 << i;
}
} else {
if (x >> i & 1) {
p |= 1 << i;
}
}
}
assert((p ^ x) == y);
seg.push_back({p, p});
return seg;
}
void Solve() {
int n;
std::cin >> n;
std::vector<int> A(n + 1), L(n + 1), R(n + 1);
for (int i = 1; i <= n; i++) {
std::cin >> A[i] >> L[i] >> R[i];
}
std::vector<int> dp(MAX);
for (int i = 0; i <= A[1]; i++) {
Add(dp[i], 1);
}
for (int i = 2; i <= n; i++) {
std::vector<int> ndp(MAX);
for (int j = 0; j < MAX; j++) {
if (dp[j] == 0) {
continue;
}
if (i % 2 == 0) {
if (j >= A[i]) {
Add(ndp[j - A[i]], dp[j]);
if (j + 1 < MAX) {
Add(ndp[j + 1], -dp[j]);
}
} else {
Add(ndp[0], dp[j]);
if (j + 1 < MAX) {
Add(ndp[j + 1], -dp[j]);
}
Add(ndp[1], dp[j]);
if (A[i] - j + 1 < MAX) {
Add(ndp[A[i] - j + 1], -dp[j]);
}
}
} else {
auto segl = GetSegXOR(j, L[i - 1] - 1);
auto segr = GetSegXOR(j, R[i - 1]);
for (auto& [l, r] : segr) {
l = std::max(0, l);
r = std::min(r, A[i]);
if (l > r) {
continue;
}
Add(ndp[l], dp[j]);
if (r + 1 < MAX) {
Add(ndp[r + 1], -dp[j]);
}
}
for (auto& [l, r] : segl) {
l = std::max(0, l);
r = std::min(r, A[i]);
if (l > r) {
continue;
}
Add(ndp[l], -dp[j]);
if (r + 1 < MAX) {
Add(ndp[r + 1], dp[j]);
}
}
}
}
for (int j = 1; j < MAX; j++) {
Add(ndp[j], ndp[j - 1]);
}
dp.swap(ndp);
}
int ans = 0;
for (int i = 0; i < MAX; i++) {
Add(ans, dp[i]);
}
std::cout << ans << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
Solve();
}
return 0;
}