「ABC 217」题解
降智了(
A、B、C、D
略。
E
降智了。
一开始发现可以用树状数组+queue(que)做掉。具体地,每次加入点在que中,如要排序,就将que中的点全部插入树状数组。询问的时候,在树状数组上二分(只是我懒打了个2log),有的话输出删掉,否则就输出、删掉que.front()。
考完发现自己降智了,这个树状数组完全可以中 \(\mathrm{set}\) 维护,不过树状数组的做法也可以带给我们一些启发。
include
include
include
include
include
define uint unsigned int
define LL long long
using namespace std;
const int MAXN = 2e5 + 5;
int Q, F[MAXN], X[MAXN], lsh[MAXN], tot, Bit[MAXN];
queue
int Lowbit(int x) { return x & (-x); }
void Update(int x, int v) { for(int i = x; i <= tot; i += Lowbit(i)) Bit[i] += v; }
int asksum(int x) { int sum_ = 0; for(int i = x; i; i -= Lowbit(i)) sum_ += Bit[i]; return sum_; }
int main() {
scanf("%d", &Q);
for(int i = 1; i <= Q; i ++) {
scanf("%d", &F[i]);
if(F[i] == 1) scanf("%d", &X[i]), lsh[++ tot] = X[i];
}
sort(lsh + 1, lsh + 1 + tot); tot = unique(lsh + 1, lsh + 1 + tot) - lsh - 1;
for(int i = 1; i <= Q; i ++) if(F[i] == 1) X[i] = lower_bound(lsh + 1, lsh + 1 + tot, X[i]) - lsh;
for(int i = 1; i <= Q; i ++) {
if(F[i] == 1) que.push(X[i]);
else if(F[i] == 2) {
int l = 1, r = tot, mid, res = -1;
while(l <= r) {
mid = (l + r) >> 1;
if(asksum(mid)) r = mid - 1, res = mid;
else l = mid + 1;
}
if(res == -1) {
printf("%d\n", lsh[que.front()]); que.pop();
}
else printf("%d\n", lsh[res]), Update(res, -1);
}
else {
while(!que.empty()) {
Update(que.front(), 1); que.pop();
}
}
}
return 0;
}
F
降智了。或许可以说,自己对 \(\mathrm{dp}\) 的积累还不够。
拿到题,发现是个匹配,显然是个区间dp。但是当你转移的时候会发现一个问题:如果你直接暴力断开,直接 \(dp[i][j]=\sum dp[i+1][k]*dp[k+1][j]\),会发生重复。就比如 \(112233\),\(1122|33\) 和 \(11|2233\) 会算两次贡献。所以这个时候可以打个 \(\mathcal {O}(n^4)\) 的 \(dp\) 套 \(dp\),考场上一直想优化直接原地去世。
对于这个匹配问题,我们 duck 不必暴力断开,而是可以枚举 \(l\) 与谁匹配,设其为 \(k\),则 \(dp_{i,j}=\sum dp_{i,k-1}\times dp_{k+1,j}*a\),这个 \(a\) 是我们要维护顺序的系数。
问题转化为,现在有序列 \(u\),序列 \(v\),还有一个数 \(w\),要求 \(w\) 必须在所有的 \(u\) 后面。由于 \(dp\) 数组里自带顺序,所以 \(u\) 和 \(v\) 只用按顺序排,所以可以把 \(u\) 和 \(w\) 看成一个序列, \(a=C_{len_u+len_v+1}^{len_v}\)。(\(len\) 即为其长度)
Therefore,\(dp_{i,j}=\sum dp_{i,k-1}\times dp_{k+1,j}*C_{j-i+1}^{j-k}\)。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define uint unsigned int
#define LL long long
using namespace std;
const int MAXN = 5005, Mod = 998244353;
int n, m, C[MAXN][MAXN], A[MAXN][MAXN];
LL jc[MAXN], inv[MAXN];
LL ans, res[MAXN];
LL GET_A(int x, int y) {
return jc[y] * inv[y - x] % Mod;
}
LL Quick_Pow(LL x, int y) {
LL ans = 1;
for(; y; y >>= 1) {
if(y & 1) ans = ans * x % Mod;
x = x * x % Mod;
}
return ans;
}
int main() {
scanf("%d%d", &n, &m);
jc[0] = 1; inv[0] = 1;
for(int i = 1; i <= n; i ++) jc[i] = jc[i - 1] * i % Mod, inv[i] = Quick_Pow(jc[i], Mod - 2);
for(int i = 0; i <= 5000; i ++) C[0][i] = 1, A[0][i] = 1;
for(int i = 1; i <= 5000; i ++) for(int j = i; j <= 5000; j ++) C[i][j] = (C[i][j - 1] + C[i - 1][j - 1]) % Mod, A[i][j] = (LL)C[i][j] * jc[i] % Mod;
for(int i = 1; i <= n; i ++) {
res[i] = 1;
for(int j = 1; j <= m; j ++) {
int t = 1 + (n - j) / m;
res[i] = res[i] * A[t][i] % Mod;
}
for(int j = 1; j <= i - 1; j ++) res[i] = (res[i] - res[j] * C[j][i]) % Mod;
LL qwq = res[i];
qwq = qwq * Quick_Pow(jc[i], Mod - 2) % Mod;
qwq = (qwq + Mod) % Mod;
printf("%lld\n", qwq);
}
// printf("%d", ans);
return 0;
}
G
降智了,大水题,考场上把 \(C[i][j]\)(组合数)打成 \(C[j][i]\) 一直过不了样例,郁郁而终。
很显然,可以 \(\mathcal {O}(1)\) 算出 modulo M 等于一个特定数的值有多少个。分开考虑后合并一下即可。
具体地,如果得到这个余数的数的个数为 \(a\),一共要分为 \(k\) 组,方案就是 \(A_k^{a}\),所有的乘起来即可,记其为 \(g_k=\prod A_k^{1 + \lfloor (n - j) / m \rfloor}\)。(值得补充的是,这里的 \(a\) 只会有两种取值,而且我们都可以算出他们分别的个数)但是很显然,这样不能保证 \(k\) 组每个组都有一个数,于是还要减去之前的 \(g\) 值,令答案为 \(ans\),聪明的同学可以看出: \(ans=(g_k-\sum_{j=1}^{j<k}g_j*C_k^j)/A_k^k\)。
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define uint unsigned int
#define LL long long
using namespace std;
const int MAXN = 405, Mod = 998244353;
int n, m;
LL dp[MAXN][MAXN], C[MAXN][MAXN];
bool vis[MAXN][MAXN];
int main() {
int x, y;
scanf("%d%d", &n, &m); n <<= 1;
for(int i = 1; i <= m; i ++) {
scanf("%d%d", &x, &y); vis[x][y] = 1;
}
for(int i = 0; i <= n; i ++) C[0][i] = 1;
for(int i = 1; i <= n; i ++) for(int j = i; j <= n; j ++) C[i][j] = (C[i - 1][j - 1] + C[i][j - 1]) % Mod;
for(int i = 1; i <= n + 1; i ++) for(int j = i - 1; j >= 1; j --) dp[i][j] = 1;
for(int len = 2; len <= n; len ++) {
for(int l = 1; l <= n - len + 1; l ++) {
int r = l + len - 1;
for(int k = l + 1; k <= r; k += 2) {
if(vis[l][k]) dp[l][r] = (dp[l][r] + dp[l + 1][k - 1] * dp[k + 1][r] % Mod * C[(r - k) / 2][(r - l + 1) / 2] % Mod) % Mod;
// if(dp[l][r]) printf("%d %d %d %lld %lld %lld %lld\n", k, l, r, dp[l][r], C[(r - k) / 2][(r - l + 1) / 2], dp[l + 1][k - 1], dp[k + 1][r]);
}
}
}
printf("%lld", dp[1][n]);
return 0;
}
H
咕咕咕。