赛后总结
这一场总体上打得稀烂,自己的水平没有完全发挥出来。直接原因就是在一道简单题上卡住了,没有灵活变通,致使自己思路也混沌了,其实后面很多题都可以做。在赛场上要及时调整,做好决策。
另外有一题数据范围没有看仔细,导致浪费了很多时间。有一题使用了静态数组,但规模开小了,少打了一个0,导致多WA了一发。这些问题以后都要注意。
理论上ACM不需要对拍,但最好还是准备好相应的模板,以备不时之需。
补题
B
赛时卡住的题,思路是对的,但是代码出现了很难发现的问题。
这种题完全不应该卡,主要原因就在于没有十分认真对待题目,思路不是很清晰就写题,越写越乱。
以后遇到这种情况需要及时跳出来,转换策略做其余的题目。
对于像这种思路很简单的题目,最重要的是理清思路,完全清晰后再写代码,不然十分容易爆错。
而写了屎山后发现是错的,要及时决断,简单题不乏尝试一下重构代码。
I
比较奇特的一道题。
关键点在于找到两种方法的不同点,分析在数据量很大情况下的差异。
甚至可以手动模拟两种方法进行生成,用自己生成的结果之间的差异作为评判标准,再和输入进行比较,进而得出结果。
多WA几发就过了
H
学到了一种高效的无遗漏的方法,用于枚举所有物品组合的合法方案,条件为满足物品重量按位或起来不超过背包重量:
对于背包重量
m
m
m按位分析,假设当前为第
i
i
i位,如果该位为
1
1
1, 那么该位将
m
m
m分成三个部分,第
0
0
0位到第
i
−
1
i - 1
i−1位、第
i
i
i位、第
i
+
1
i+1
i+1位到最高位。对于要选择的物品组合,我们可以强制重量按位或的和的第
i
i
i位为
0
0
0, 第
0
0
0位到第
i
−
1
i-1
i−1位随意,第
i
+
1
i+1
i+1位到最高位必须是
m
m
m的“子集”(也就是按位或和某一位为
1
1
1, 那么
m
m
m对应的这一位也必须为
1
1
1)。
这样可以不重不漏地枚举所有情况,注意要考虑
i
=
−
1
i=-1
i=−1的情况(即按位或和所有位都必须是
m
m
m所有位的“子集”)。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ai = array<int, 2>;
using al = array<ll, 2>;
void solve() {
int n, m;
cin >> n >> m;
vector<int> w(n + 1);
vector<ll> v(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> v[i] >> w[i];
}
bitset<30> a(m);
ll ans = 0LL;
auto choose = [&](int p) {
ll now = 0LL;
for (int i = 1; i <= n; ++i) {
bitset<30> b(w[i]);
if (p != -1 && b[p]) continue;
bool fl = 1;
for (int j = p + 1; j < 30; ++j)
if (b[j] && !a[j]) {fl = 0; break;}
if (!fl) continue;
now += v[i];
}
ans = max(ans, now);
};
for (int i = 0; i < 30; ++i) {
if (a[i]) {
choose(i);
}
}
choose(-1);
cout << ans << '\n';
}
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int _ = 1;
cin >> _;
while (_--)
solve();
return 0;
}
D
这一题破题点在于数据范围,发现询问的数字不大,而数组长度很大,所以可以进行分类讨论。
- 当数组长度 n > 30 n > 30 n>30时,因为询问的数字绝对值最大为1e9, 且 2 30 > 1 e 9 2^{30} > 1e9 230>1e9, 所以整个数组绝对值非1的个数不能超过 29 29 29, 进而对于每一个数,我们先判断把它变成1或-1后整个数组绝对值非1的个数是否符合要求,如果符合,用一个set记录改变之后的结果。在这种限制条件下,能改变的数比较少。
- 当数组长度不超过 30 30 30时,我们来寻找一下改变的值的边界条件,当数组中存在至少两个绝对值大于等于 1 e 9 \sqrt{1e9} 1e9的数时,这样乘积肯定会出界。所以我们只需要计算一下第二大的数到 1 e 9 \sqrt{1e9} 1e9的距离和第二小的数到 − 1 e 9 -\sqrt{1e9} −1e9的距离,将这两个距离作为上下界,枚举所有情况即可,最多有 2 × 1 e 9 2 \times \sqrt{1e9} 2×1e9种情况,不会超时。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int main() {
cin.tie(0) -> sync_with_stdio(0);
int n, Q;
cin >> n >> Q;
vector<ll> a(n + 1);
map<ll, int> cnt;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
cnt[a[i]] += 1;
}
set<ll> s{0LL};
auto change = [&](ll x) {
ll ret = 1;
for (int i = 1; i <= n; ++i) {
ret *= a[i] + x;
if (abs(ret) > 1e9) return;
}
s.insert(ret);
};
if (n > 30) {
map<int, bool> vis;
for (int i = 1; i <= n; ++i) {
if (vis.find(a[i]) != vis.end()) continue;
vis[a[i]] = 1;
ll x = 1 - a[i], y = -1 - a[i];
int now = n - (cnt[a[i]] + cnt[-1LL - x]);
if (now < 30) change(x);
now = n - (cnt[a[i]] + cnt[1LL - y]);
if (now < 30) change(y);
}
} else {
sort(a.begin() + 1, a.end());
ll L, R;
R = 4e4 - a[n - 1];
L = -4e4 - a[2];
// cout << L << ' ' << R << '\n';
for (ll i = L; i <= R; ++i)
change(i);
}
// cout << s.size() << '\n';
while (Q--) {
ll M;
cin >> M;
if (s.count(M)) cout << "Yes\n";
else cout << "No\n";
}
return 0;
}