Solution -「ABC 217」EFG
E - Sorting Queries
将所有的插入操作先存进一个临时序列。
在遇到排序操作时再处理这些序列里的元素。
具体来说就是直接丢进优先队列。也可以学我赛时直接 copy D 的 code 来改。
#include <queue>
#include <cstdio>
using namespace std;
typedef long long LL;
LL Max(LL x, LL y) { return x > y ? x : y; }
LL Min(LL x, LL y) { return x < y ? x : y; }
LL Abs(LL x) { return x < 0 ? -x : x; }
int read() {
int k = 1, x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
}
LL read_LL() {
int k = 1;
LL x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
}
void write(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(LL x, char s) {
write(x);
putchar(s);
}
const int MAXN = 2e5 + 5;
int num[MAXN], len = 0, tot = 1;
priority_queue<int, vector<int>, greater<int> > q;
int main() {
int n = read();
for(int i = 1, opt; i <= n; i++) {
opt = read();
if(opt == 1) {
int x = read();
num[++len] = x;
}
else if(opt == 2) {
if(!q.empty()) {
print(q.top(), '\n');
q.pop();
}
else {
print(num[tot], '\n');
tot++;
}
}
else {
for(int i = tot; i <= len; i++)
q.push(num[i]);
tot = 1, len = 0;
}
}
return 0;
}
F - Make Pair
小清新的区间 dp
题。
相邻删除这样的操作就应该往区间 dp
上去想。
定义 f ( i , j ) f(i, j) f(i,j) 表示将区间 [ i , j ] [i, j] [i,j] 匹配完的方案数,显然需要满足 2 ∣ r − l + 1 2 \mid r - l + 1 2∣r−l+1。
可得:
f ( i , j ) = ∑ k ∈ ( l , r ] , 2 ∣ k − i + 1 f ( i + 1 , k ) × f ( k + 1 , j ) × ( j − i + 1 2 j − k + 1 2 ) f(i, j) = \sum_{k \in (l, r], 2 | k - i + 1} f(i + 1, k) \times f(k + 1, j) \times \binom{\frac {j - i + 1} {2}}{\frac {j - k + 1} {2}} f(i,j)=k∈(l,r],2∣k−i+1∑f(i+1,k)×f(k+1,j)×(2j−k+12j−i+1)
其中 k k k 同学和 i i i 同学关系友好,且对于任意 i i i,若 0 ≤ i ≤ n 0 \leq i \leq n 0≤i≤n,有 f ( i , i + 1 ) = 1 f(i, i + 1) = 1 f(i,i+1)=1。
其中组合数可以理解为我们共有 j − i + 1 2 \frac {j - i + 1} {2} 2j−i+1 个盒子(盒子间被认为是不同的),且我们有 k − i − 1 2 \frac {k - i - 1} {2} 2k−i−1 个 a a a 球, 1 1 1 个 b b b 球,和 j − k 2 \frac {j - k} {2} 2j−k 个 c c c 球(同种类的球被认为是相同的,这是因为在上面的转移柿子中我们已经考虑了球种类内部的情况),且 b b b 球放入的盒子编号必须大于任意一个 a a a 球放入的盒子编号。
感性理解一下其实就是我们在所有的盒子中选 j − k 2 \frac {j - k} {2} 2j−k 个出来放 c c c 球。
时间复杂度 O ( n 3 ) O(n^3) O(n3)。
#include <cstdio>
typedef long long LL;
int Max(int x, int y) { return x > y ? x : y; }
int Min(int x, int y) { return x < y ? x : y; }
int Abs(int x) { return x < 0 ? -x : x; }
int read() {
int x = 0, k = 1;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while ('0' <= s && s <= '9') {
x = (x << 3) + (x << 1) + (s - '0');
s = getchar();
}
return x * k;
}
void write(LL x) {
if (x < 0) {
x = -x;
putchar('-');
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(LL x, char s) {
write(x);
putchar(s);
}
const int MAXN = 4e2 + 5;
const int mod = 998244353;
bool w[MAXN][MAXN];
LL q[MAXN], sq[MAXN], dp[MAXN][MAXN];
LL A (int x, int y) { return q[x] * sq[x - y] % mod;}
LL C (int x, int y) { return A(x, y) * sq[y] % mod;}
LL Quick_Pow(LL a, LL b) {
LL res = 1;
while(b) {
if(b & 1)
res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res % mod;
}
int main() {
int n = read(), m = read();
q[0] = 1, sq[0] = 1;
for(int i = 1; i <= (n << 1); i++) {
q[i] = q[i - 1] * i % mod;
sq[i] = Quick_Pow(q[i], mod - 2);
}
for(int i = 1, a, b; i <= m; i++) {
a = read(), b = read();
w[a][b] = w[b][a] = true;
}
for(int i = 1; i <= (n << 1) + 1; i++)
dp[i][i - 1] = 1;
for(int len = 2; len <= (n << 1); len += 2)
for(int l = 1; l + len - 1 <= (n << 1); l++) {
int r = l + len - 1;
for(int k = l + 1; k <= r; k += 2)
if(w[l][k])
dp[l][r] = (dp[l][r] + dp[l + 1][k - 1] * dp[k + 1][r] % mod * C(len / 2, (r - k + 1) / 2) % mod) % mod;
}
print(dp[1][n << 1], '\n');
return 0;
}
G - Groups
还是一道 dp
呢。
我们定义 f ( i , j ) f(i, j) f(i,j) 表示分完前 i i i 个数且当前分了 j j j 个组。
考虑当前数的两种决策。
第一种,新开一个组,即方案数为 f ( i − 1 , j − 1 ) f(i - 1, j - 1) f(i−1,j−1)。
第二种,不开新的组而是丢进已存在的组,不难发现当前数可以丢入的组共有 j − g ( i , i m o d m ) j - g(i, i \bmod m) j−g(i,imodm) 个,其中 g ( x , y ) = ∑ i = 1 x [ i m o d m = y ] g(x, y) = \sum_{i = 1}^{x} [i \bmod m = y] g(x,y)=∑i=1x[imodm=y]。
故可得, f ( i , j ) = f ( i − 1 , j − 1 ) + f ( i − 1 , j ) × ( j − g ( i , i m o d m ) ) f(i, j) = f(i - 1, j - 1) + f(i - 1, j) \times (j - g(i, i \bmod m)) f(i,j)=f(i−1,j−1)+f(i−1,j)×(j−g(i,imodm))。
初始状态为 f ( 0 , 0 ) = 1 f(0, 0) = 1 f(0,0)=1。其他的比如 g ( x , y ) g(x, y) g(x,y) 的转移啦、答案状态啦就很简单了嘛,时间复杂度 O ( n 2 ) O(n^2) O(n2)。
#include <cstdio>
typedef long long LL;
LL Max(LL x, LL y) { return x > y ? x : y; }
LL Min(LL x, LL y) { return x < y ? x : y; }
LL Abs(LL x) { return x < 0 ? -x : x; }
int read() {
int k = 1, x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
}
LL read_LL() {
int k = 1;
LL x = 0;
char s = getchar();
while (s < '0' || s > '9') {
if (s == '-')
k = -1;
s = getchar();
}
while (s >= '0' && s <= '9') {
x = (x << 3) + (x << 1) + s - '0';
s = getchar();
}
return x * k;
}
void write(LL x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
write(x / 10);
putchar(x % 10 + '0');
}
void print(LL x, char s) {
write(x);
putchar(s);
}
const int MAXN = 5e3 + 5;
const int mod = 998244353;
int t[MAXN];
LL dp[MAXN][MAXN];
int main() {
int n = read(), m = read();
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++)
dp[i][j] = (dp[i - 1][j - 1] + dp[i - 1][j] * ((j - t[i % m] + mod) % mod) % mod) % mod;
t[i % m]++;
}
for(int i = 1; i <= n; i++)
print(dp[n][i], '\n');
return 0;
}