356
学习:二进制枚举以及加强搜索,pbds,缩点思想。
A - Subsegment Reverse
给你正整数
对于长度为
打印操作后的序列。
void solve() {
int n, l, r;cin >> n >> l >> r;
vector<int>a(n + 1);
for (int i = 1;i <= n;i++)a[i] = i;
reverse(a.begin() + l, a.begin() + r + 1);
for (int i = 1;i <= n;i++)cout << a[i] << " ";
}
B - Nutrients
高桥非常注重健康,他担心自己是否从饮食中摄入了足够的
对于
今天,他吃了
确定他是否达到了所有
模拟:
#define int long long
int s[110][110];
void solve() {
int n, m;cin >> n >> m;
vector<int> a(m + 1);
for (int i = 1;i <= m;i++)cin >> a[i];
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= m;j++) {
cin >> s[i][j];
}
}
for (int i = 1;i <= m;i++) {
int sum = 0;
for (int j = 1;j <= n;j++) {
sum += s[j][i];
}
if (sum < a[i]) {
cout << "No\n";return;
}
}
cout << "Yes\n";
}
C - Keys
你有
其中一些是真钥匙,其他的是假钥匙。
有一扇门,门 X,你可以插入任意数量的钥匙。只有插入至少
你已经对这些钥匙进行了
- 您将
把 把钥匙插入了 X 号门。 - 测试结果用一个英文字母
表示。 o 表示在 -th 测试中门 X 打开了。 x 表示在 -th测试中门X没有打开。
有
给定的测试结果有可能是错误的,没有一个组合满足条件。在这种情况下,报告
根据题目意思就是让枚举钥匙的所有可能性,对于每一种可能性带入给定的条件看是否满足,若满足计为一种答案
DFS / 二进制枚举:时间复杂度:
int a[110][20];
void solve() {
int n, m, tar;cin >> n >> m >> tar;
vector<int> c(m + 1), is(m + 1);
for (int i = 1;i <= m;i++) {
cin >> c[i];
for (int j = 1;j <= c[i];j++) {
cin >> a[i][j];
}
char op;cin >> op;
is[i] = (op == 'o');
}
int ans = 0;
for (int i = 0;i < (1 << n);i++) {
vector<int> pan(n + 1);
for (int j = 0;j < n;j++) {
if (i & (1 << j)) {
pan[j + 1] = 1;
}
}
int ok = 1;
for (int j = 1;j <= m;j++) {
int sum = 0;
for (int k = 1;k <= c[j];k++) {
if (pan[a[j][k]])sum++;
}
if (sum >= tar && !is[j]) ok = 0;
if (sum < tar && is[j]) ok = 0;
}
if (ok)ans++;
}
cout << ans << '\n';
}
D - Masked Popcount
给定整数
位运算:
根据
区间
很容易计算:对于第
#define int long long
constexpr int mod = 998244353;
void solve() {
int n, m;cin >> n >> m;
int sum = 0;
n++;
for (int i = 0;i <= __lg(m);i++) {
if ((m >> i) & 1) {
int x = n / (1ll << (i + 1));
int y = n % (1ll << (i + 1));
sum = (sum + x * (1ll << i) + max(y - (1ll << i), 0ll)) % mod;
}
}
cout << sum << '\n';
}
E - Max/Min
给定
当某个数字相同的个数为
当两个数不同时,对于
先用一个数组
对于每个
jiangly:
void solve() {
int n;cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
}
const int M = *max_element(a.begin(), a.end());
vector<int> c(M + 1);
for (auto x : a) {
c[x]++;
}
vector<int> s(M + 1);
for (int i = 1; i <= M; i++) {
s[i] = s[i - 1] + c[i];
}
int ans = 0;
for (int x = 1; x <= M; x++) {
ans += c[x] * (c[x] - 1) / 2;
for (int y = x; y <= M; y += x) {
int v = s[min(y + x - 1, M)] - s[max(x, y - 1)];
ans += c[x] * v * (y / x);
}
}
cout << ans << "\n";
}
F - Distance Component Size Query
给你一个整数
1 x
: 给出一个整数。如果 在 中,则从 中删除 。否则,在 中加入 。 2 x
: 给出一个在中的整数 。考虑一个图,图中的顶点是 中的数,且当且仅当两个数之间的绝对差最多为 时,这两个数之间有一条边。请打印包含 的连通部分中的顶点数。
主要思路就是建立两个集合,一个记录现在的集合,一个记录缩点后的集合。
缩点就是将每个联通块且将最大的数字缩成一个点。
这样之后若要查询 x 连通块的大小,则就是这个连通块在第一个集合中的位置减去上一个连通块在第一个集合中的位置。
ordered_set<int> s1;set<int> s2; // s1是所有点的点集 s2会把一个连通块的点缩成最大那个点
// (s1要求rank,所以必须用PBDS,s2普通set也可以)
void solve() {
ll q, k;cin >> q >> k;
// 这里插几个哨兵是为了方便出题,边界上的prev next 就能统一了
s1.insert(-4e18), s1.insert(4e18);
s2.insert(-4e18), s2.insert(4e18);
while (q--) {
ll op, x;cin >> op >> x;
if (op == 1) {
if (s1.find(x) == s1.end()) {
auto it = s1.insert(x).first;
ll w1 = *prev(it), w2 = *next(it); // x左右两个点编号
if (x - w1 <= k) s2.erase(w1);
if (w2 - x <= k)
; // x就不用插入s2了
else
s2.insert(x);
} else {
auto it = s1.find(x);
ll w1 = *prev(it), w2 = *next(it); // x左右两个点编号
s1.erase(x), s2.erase(x); // x在s2里不存在也没影响
if (w2 - w1 > k) s2.insert(w1); // 让w1复活 (原来是活的也不影响)
}
} else {
auto it = s2.lower_bound(x); // 找到x所在连通块最大的点
cout << s1.order_of_key(*it) - s1.order_of_key(*prev(it)) << '\n';
}
}
}