1001 战斗爽

题解:
纯模拟,用一个heap和set去记录剩下的怪物中的攻击力最高的和还没有收到超过k次伤害的
cpp
struct enemy{
int atk, hp, cnt, id;
bool operator> (const struct enemy& w) const {
if (hp == w.hp) {
if (atk == w.atk) {
return id > w.id;
} else {
return atk > w.atk;
}
} else {
return hp > w.hp;
}
}
};
void solve()
{
int n, u, k, hq;
cin >> n >> u >> k >> hq;
vector<enemy> e(n + 1);
priority_queue<enemy, vector<enemy>, greater<enemy>> heap;
set<PII> S;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
e[i] = {a, b, 0, i};
heap.push(e[i]);
S.insert({a, i});
}
int ans = 0;
while(S.size() && heap.size()) {
if (hq <= 0) break;
auto [atk, hp, cnt, id] = heap.top();
heap.pop();
hp -= (cnt == 0 ? (int)u : u / 2);
if (hp <= 0) {
S.erase({atk, id});
ans++;
} else if (cnt + 1 == k) {
} else {
heap.push({atk, hp, cnt + 1, id});
}
if (!S.empty()) {
auto it = S.end();
it--;
hq -= it->first;
}
}
cout << ans << endl;
}
1003 洞察

题解:
跟上一场的位运算题目的思考方式好像,都是从高位向低位进行枚举去划分。
首先明确一个性质:a ^ b = c ------> a = b ^ c
依据上述性质,我们可以把不等式 px ^ c <= v 转化为 px <= v ^ c,这里不要考虑不等式,需要考虑的是对于每一个 px ^ c 总有一个 v ^ c 来进行映射,所以可以这么转化。
然后,我们的任务就转化成了,求出有多少个 px 是在 1 ~ v ^ c 的范围内的,这里应该怎么计算呢??
跟上一场的1001一样,我们从高位到低位枚举,假设目前枚举到 i 位,则按照从 i ~ 最高位 都贴近上界的方式来进行集合的划分,如果第i位为1,则这一位取0,然后低位随便取,一定满足在 1 ~ v^c 的范围内。
知道了一个范围,知道了x的表达式,我们按照等差数列的求和公式,以及前缀和的公式,就可以知道一个区间内的 x 的个数。
cpp
void solve()
{
int k, b, c, v;
cin >> k >> b >> c >> v;
int led = 0, ans = 0;
if ((v ^ c) - b >= 0 && ((v ^ c) - b) % k == 0) ans++;
for (int i = 59; i >= 0; i--){
if (v >> i & 1) {
int u = (led ^ c) & ((1LL << 60) - (1LL << i));
int l = u - 1, r = u + ((1LL << i) - 1);
if (r >= b) {
ans += (r - b) / k + 1;
}
if (l >= b) {
ans -= (l - b) / k + 1;
}
}
led |= (v >> i & 1) << i;
}
cout << ans << endl;
}
这里有几个位运算的操作解释一下
cpp
int u = (led ^ c) & ((1LL << 60) - (1LL << i));
led 表示的就是之前位全部贴近上界的值
((1LL << 60) - (1LL << i)) 表示二进制表示下,当前位及高位都为1,低位均为0的一个数字,然后和之前的数字取&就表示低位全部取0,高位保留。
而且由于led还没有更新,所以本位i也是取0的,则当前区间就是 [u, u + (1LL << i ) - 1],后者就是后面所有位都取1的情况,然后计算就行了。
1005 持家

感觉最签的一题
题解:
先用打折券肯定更优,然后直接枚举所有方案就行了,用一个前缀积和一个前缀和来加速枚举过程
cpp
void solve()
{
double p;
int n, k;
cin >> p >> n >> k;
vector<double> a(1), b(1);
for (int i = 1; i <= n; i++) {
int op, x;
cin >> op >> x;
if (op == 0) {
a.push_back(x);
} else {
b.push_back(x);
}
}
sort(a.begin() + 1, a.end());
sort(b.begin() + 1, b.end(), greater<double>());
vector<double> prea(n + 1), preb(n + 1, 0);
prea[0] = 1;
for (int i = 1; i < b.size(); i++) {
preb[i] = preb[i - 1] + b[i];
}
for (int i = 1; i < a.size(); i++) {
prea[i] = prea[i - 1] * a[i] / 10.0;
}
double ans = max(p - preb[min((int)b.size() - 1LL, (int)k)], 0.00);
for (int i = 0; i <= k; i++) {
if (i >= a.size()) break;
ans = min(ans, max((double)p * prea[i] - preb[min((int)(b.size() - 1LL), (int)k - i)], 0.00));
}
printf("%.2lf\n", ans);
}
1006 进步

题解:
树状数组模板题
cpp
template <typename T>
class Fenwick {
public:
int n;
std::vector<T> a;
Fenwick(int n_ = 0) {
init(n_);
}
void init(int n_) {
n = n_;
a.assign(n + 1, T{});
}
void add(int x, const T &v) {
for (int i = x; i <= n; i += i & -i) {
a[i] = (a[i] + v);
}
}
// 查询位置 [1, x] 的前缀和
T sum(int x) {
T ans{};
for (int i = x; i > 0; i -= i & -i) {
ans = (ans + a[i]);
}
return ans;
}
// 查询区间 [l, r] 的区间和
T rangeSum(int l, int r) {
return sum(r) - sum(l - 1);
}
int select(const T &k) {
int x = 0;
T cur{};
for (int i = 1 << std::__lg(n); i; i /= 2) {
if (x + i <= n && cur + a[x + i] <= k) {
x += i;
cur = cur + a[x];
}
}
return x;
}
};
void solve()
{
int n, q;
cin >> n >> q;
vector<int> a(n + 1);
Fenwick<int> fen(n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
fen.add(i, a[i]);
}
int ans = 0, cnt = 1;
for (int i = 1; i <= q; i++) {
int op, x, y;
cin >> op >> x >> y;
if (op == 1) {
fen.add(x, -(a[x]));
a[x] = y;
fen.add(x, a[x]);
} else if (op == 2) {
int sl = fen.sum(x - 1), sr = fen.sum(y);
int res = sr / 100 - sl / 100;
ans ^= (cnt * res);
cnt++;
}
}
cout << ans << endl;
}
1008 制衡

题解:
WA了三发的DP
cpp
void solve()
{
int n, k;
cin >> n >> k;
vector<vector<int>> a(n + 1, vector<int>(k + 1));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= k; j++) {
cin >> a[i][j];
}
}
vector<vector<int>> f(n + 1, vector<int>(k + 1, -INF));
f[0][0] = f[0][1] = 0;
for (int i = 1; i <= n; i++) f[i][1] = f[i - 1][1] + a[i][1];
for (int i = 1; i <= n; i++) {
vector<int> pre(k + 1, -INF);
for (int j = 1; j <= k; j++) {
pre[j] = max(pre[j - 1], f[i - 1][j]);
}
for (int j = 2; j <= k; j++) {
f[i][j] = pre[j] + a[i][j];
}
}
int ans = 0;
for (int i = 1; i <= k; i++) {
ans = max(ans, f[n][i]);
}
cout << ans << endl;
}
1010 图之图

题解:
这种题之前基本没有见过,思路就感觉很......假。
考察的是根号分块方面的知识。
cpp
void solve()
{
int n, m, c;
cin >> n >> c;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
vector<int> g(c + 1, 0), f(n + 1, 0), w(c + 1, 0);
vector<vector<int>> e(c + 1), eh(c + 1);
f[1] = 1;
cin >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
e[u].push_back(v);
if (u != v) e[v].push_back(u);
}
int S = sqrt(m);
for (int u = 1; u <= c; u++) {
for (auto v : e[u]) {
if (e[v].size() > S) {
eh[u].push_back(v);
}
}
}
for (int i = 1; i <= n; i++) {
(f[i] += w[a[i]]) %= mod;
for (auto v : eh[a[i]]) {
(f[i] += g[v]) %= mod;
}
(g[a[i]] += f[i]) %= mod;
if (e[a[i]].size() <= S) {
for (auto v : e[a[i]]) {
(w[v] += f[i]) %= mod; // 出度,所有指向的边都++
}
}
}
cout << f[n] << endl;
}