T1. 乾坤
容易有一个贪心,按费用从小到大使用符咒,符咒去消灭能消灭的能力值最大的妖怪这显然是对的。可以用小根堆来实现。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
using P = pair<int, int>;
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int n, m;
cin >> n >> m;
vector<int> a(n);
rep(i, n) cin >> a[i];
sort(a.begin(), a.end(), greater<int>());
vector<P> spells(m);
rep(i, m) {
int d, c;
cin >> d >> c;
spells[i] = {d, c};
}
sort(spells.begin(), spells.end(), [](const P& a, const P& b) {
if (a.first == b.first) {
return a.second < b.second;
}
return a.first > b.first;
});
int j = 0;
ll ans = 0;
priority_queue<int, vector<int>, greater<int>> q;
for (int x : a) {
while (j < m and spells[j].first >= x) {
q.push(spells[j].second);
j++;
}
if (!q.size()) {
cout << "loss\n";
return 0;
}
ans += q.top(); q.pop();
}
cout << ans << "\n";
return 0;
}
T2. 赛道长度
希望暴力地对每个点维护 std::bitset 来统计以其为起点的每个长度是否出现过,只考虑子树 \(S_v\) 里的贡献是简单的,这个 bitset 记为 dp[v],接下来需要统计子树外的点 \(V \backslash S_v\) 中的贡献,这个是从父亲的 \(dp\) 中“挖掉” \(S_v\) 得到的,实际上做不到挖掉这个操作,于是在父亲处把统计 \(dp\) 的贡献转为维护一下前后缀或就可以了。时空复杂度均为 \(O(\frac{n^2}{w})\),可以通过。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int N = 50005, M = 26000;
int a[N];
vector<int> to[N];
bitset<M> dp[N], pre[N], suf[N], bs[N], tbs[N];
int ans[N];
void dfs(int v, int p=-1) {
if (a[v]) dp[v].set(0);
for (int u : to[v]) {
if (u == p) continue;
dfs(u, v);
dp[v] |= dp[u]<<1;
}
}
void dfs2(int v, int p=-1) {
if (a[v]) bs[v].set(0);
int tot = 0;
vector<int> vc(1, -1);
pre[0].reset();
for (int u : to[v]) {
if (u == p) continue;
++tot;
vc.push_back(u);
pre[tot] = pre[tot-1] | dp[u]<<2;
}
suf[tot+1].reset();
for (int i = tot; i; --i) {
suf[i] = suf[i+1] | dp[vc[i]]<<2;
}
for (int i = 1; i <= tot; ++i) {
tbs[vc[i]] = pre[i-1] | suf[i+1];
}
for (int i = 1; i <= tot; ++i) {
int u = vc[i];
ans[u] = (bs[v]<<1 | tbs[u] | dp[u]).count();
bs[u] = bs[v]<<1 | tbs[u];
dfs2(u, v);
}
}
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int n;
cin >> n;
rep(i, n) cin >> a[i];
rep(i, n-1) {
int u, v;
cin >> u >> v;
--u; --v;
to[u].push_back(v);
to[v].push_back(u);
}
dfs(0);
ans[0] = dp[0].count();
dfs2(0);
rep(i, n) cout << ans[i] << '\n';
return 0;
}
T3. 前缀最值
先忽略只确定了前 \(n\) 项的问题,考虑这个前缀最大值数组 \(f(q)\),由于其总是有序的,所以 \(f(q_1) = f(q_2)\) 当且仅当 \(f(q_1)\),\(f(q_2)\) 对应的桶相等,记这个桶为 \(c\)(\(c_i\) 表示 \(i\) 出现在前缀最大值数组中的次数),则我们有 \(c_i \geqslant 0\),\(c_1 + c_2 + \cdots + c_i \leqslant i\),\(c_1 + c_2 + \cdots + c_{2n} = 2n\)。
只要给出满足条件的 \(c\),我们总能找到 \(q\) 使得 \(f(q)\) 的桶是 \(c\),于是就可以对 \(c\) 进行折线计数了,具体如下:
从 \((0, 0)\) 出发,对于每个 \(1 \leqslant i \leqslant 2n\),向右移动一步,向上移动 \(c_i\) 步,且始终不越过 \(y=x\),最终到达 \((2n, 2n)\) 的方案数。
现在再加入确定了前 \(n\) 项的限制,我们变成了从 \((m, n)\) 出发,其中 \(m=\max(p_1, p_2, p_n)\)。不考虑不能越过 \(y=x\) 的限制的方案数是 \(\binom{3n-m}{n}\),越过的总能在第一次越过时沿着 \(y=x\) 把之后的路线全部翻折,等价于从 \((n-1, m+1)\) 出发的所有到达 \((2n, 2n)\) 的无约束路径条数,于是减去这样的 \(\binom{3n-m}{n+1}\) 即可。
于是答案为 \(\binom{3n-m}{n} - \binom{3n-m}{n+1}\)
时间复杂度为 \(O(n)\)
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
const int mod = 998244353;
//const int mod = 1000000007;
struct mint {
ll x;
mint(ll x=0):x((x%mod+mod)%mod) {}
mint operator-() const {
return mint(-x);
}
mint& operator+=(const mint a) {
if ((x += a.x) >= mod) x -= mod;
return *this;
}
mint& operator-=(const mint a) {
if ((x += mod-a.x) >= mod) x -= mod;
return *this;
}
mint& operator*=(const mint a) {
(x *= a.x) %= mod;
return *this;
}
mint operator+(const mint a) const {
return mint(*this) += a;
}
mint operator-(const mint a) const {
return mint(*this) -= a;
}
mint operator*(const mint a) const {
return mint(*this) *= a;
}
mint pow(ll t) const {
if (!t) return 1;
mint a = pow(t>>1);
a *= a;
if (t&1) a *= *this;
return a;
}
// for prime mod
mint inv() const {
return pow(mod-2);
}
mint& operator/=(const mint a) {
return *this *= a.inv();
}
mint operator/(const mint a) const {
return mint(*this) /= a;
}
};
istream& operator>>(istream& is, mint& a) {
return is >> a.x;
}
ostream& operator<<(ostream& os, const mint& a) {
return os << a.x;
}
struct modinv {
int n; vector<mint> d;
modinv(): n(2), d({0,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(-d[mod%n]*(mod/n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} invs;
struct modfact {
int n; vector<mint> d;
modfact(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*n), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} facts;
struct modfactinv {
int n; vector<mint> d;
modfactinv(): n(2), d({1,1}) {}
mint operator()(int i) {
while (n <= i) d.push_back(d.back()*invs(n)), ++n;
return d[i];
}
mint operator[](int i) const { return d[i];}
} ifacts;
mint comb(int n, int k) {
if (n < k || k < 0) return 0;
return facts(n)*ifacts(k)*ifacts(n-k);
}
void solve() {
int n;
cin >> n;
vector<int> p(n);
rep(i, n) cin >> p[i];
int m = 0;
rep(i, n) m = max(m, p[i]);
mint ans = comb(3*n-m, n) - comb(3*n-m, n+1);
cout << ans << '\n';
}
int main() {
int t;
cin >> t;
while (t--) solve();
return 0;
}
浙公网安备 33010602011771号