AtCoder Beginner Contest 215
菜的本質暴露的一覽無餘(
一場 ABC 一場 ARC 排名都 1k+,直接掉了 200 多分 QwQ
H 是比較神仙的數數題,所以鍋了。
\(A\sim D\)
\(A\sim C\) 就簡單模擬,\(D\) 簡單篩一下。
\(E\)
給定字串序列,只包含前 \(10\) 個大寫字母 \(A\sim J\),求合法的子序列個數。
一個子序列是合法的,當且僅當它當中的相同字元都是連續的,例如
AABCD
合法,但ABCDA
不合法。
真·考場降智,認為 \(2^{10}\) 超級大,根本不可能狀壓/fad
顯然就簡單狀壓 DP,設 \(f(i,S,x)\)
簡單轉移即可。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N = 1010; const LL MOD = 998244353; int n; char str[N]; LL f[N][1 << 10][10]; void P(LL &x, LL y) {x = (x + y) % MOD;} int main() { scanf("%d %s", &n, str + 1); for(int i = 1; i <= n; i ++) { int c = str[i] - 'A'; for(int s = 0; s < (1 << 10); s ++) for(int t = 0; t < 10; t ++) if(f[i - 1][s][t]) { P(f[i][s][t], f[i - 1][s][t]); if(!(s >> c & 1) || (t == c)) P(f[i][s | (1 << c)][c], f[i - 1][s][t]); } P(f[i][1 << c][c], 1); } LL ans = 0; for(int s = 0; s < (1 << 10); s ++) for(int t = 0; t < 10; t ++) P(ans, f[n][s][t]); printf("%lld\n", ans); return 0; }
\(F\)
求平面最遠點對,其中兩點 \((x_1,y_1),(x_2,y_2)\) 距離定義為 \(\min(|x_1-x_2|,|y_1-y_2|)\)。
因為沒想出來 E 所以比賽後期思維挺亂的,跳到這道題之後一直想著分治做法。
實際上是簡單的二分答案,將點按 \(x\) 升序排序,然後雙指標掃描一遍。
得到裡當前點 \(x\) 的差距最小的,\(x\) 維上的距離 \(\geq mid\) 的點,預處理一個 \(y\) 的字尾 \(\min,\max\) 即可簡單判斷。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 2e5 + 10; int n, Mx[N], Mn[N]; struct Node{int x, y;} a[N]; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar(); return x * f; } bool cmp(Node a, Node b) {return a.x < b.x;} int Abs(int x) {return x < 0 ? - x : x;} bool Chck(int mid) { int r = 1; for(int l = 1; l <= n; l ++) { while(r <= n && a[r].x - a[l].x < mid) r ++; if(r > n) return false; if(r <= n) { if(Abs(a[l].y - Mx[r]) >= mid) return true; if(Abs(a[l].y - Mn[r]) >= mid) return true; } } return false; } int main() { n = read(); for(int i = 1; i <= n; i ++) a[i].x = read(), a[i].y = read(); sort(a + 1, a + n + 1, cmp); Mx[n] = Mn[n] = a[n].y; for(int i = n - 1; i >= 1; i --) Mx[i] = max(Mx[i + 1], a[i].y), Mn[i] = min(Mn[i + 1], a[i].y); int L = 0, R = Mx[1] - Mn[1]; while(L < R) { int mid = (L + R + 1) >> 1; if(Chck(mid)) L = mid; else R = mid - 1; } printf("%d\n", L); return 0; }
\(G\)
給定長度為 \(N\) 的序列,每個點有顏色 \(c_i\),對於每個 \(1\leq K\leq N\),求隨機選出 \(K\) 個點的期望不同顏色個數。
根據期望的線性性質,可以將問題變為每種顏色選或不選,形式化一點:
設 \(X(i)\) 表示是否出現了顏色 \(i\),出現了為 \(1\) 否則為 \(0\)。
\[Ans=E(\sum\limits_{i=1}^c X(i))=\sum\limits_{i=1}^cE(X(i)) \]每個 \(E(X(i))\) 相當於顏色 \(i\) 在隨機選的 \(K\) 個點中至少出現一次的概率,設顏色 \(i\) 的出現次數為 \(n_i\),那麼:
\[E(X(i))=(\binom{N}{K}-\binom{N-n_i}{K})/\binom{N}{K} \]那麼每次的就有:
\[Ans=\frac{\sum\limits_{i=1}^c (\binom{N}{K}-\binom{N-n_i}{K})}{\binom{N}{K}} \]對於每個 \(K\),都需要 \(O(c)\) 的時間計算,時間複雜度最劣為 \(O(n^2)\)。
繼續分析性質,不難發現 \(\sum\) 裡的值在 \(K\) 確定的情況下,只和 \(n_i\) 的值有關。
那麼考慮將 \(n_i\) 相等的顏色縮為同種顏色,同時計算,設最後剩下 \(C\) 種顏色,第 \(i\) 種顏色都由原本的 \(a_i\) 種縮成。
那麼有:
\[Ans=\frac{\sum\limits_{i=1}^C a_i(\binom{N}{K}-\binom{N-n_i}{K})}{\binom{N}{K}} \]這個看上去沒啥用,但是嚴謹分析一下 \(C\) 的數量級別。
對於 \(n_i>\sqrt N\) 的顏色,顯然至多有 \(\sqrt N\) 種,否則總個數就 \(>N\) 了,顯然不可能。
對於 \(n_i\leq \sqrt N\) 的顏色,至多也就 \(0\sim \sqrt N\) 這麼多種。
故總個數不可能超過 \(2\sqrt N\),而且遠小於這個上界,故總時間複雜度 \(O(N\sqrt N)\)。
小插曲:有人在 AtCoder 上釋出了 \(O(n\log n)\) 的 Poly 做法,把標算爆了,但是看不懂。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
#define X first
#define Y second
#define MP make_pair
typedef pair<int, int> PII;
typedef long long LL;
const int N = 5e4 + 10;
const LL MOD = 998244353;
int n, t, a[N], b[N], sum[N], num[N];
LL fac[N], inv[N];
vector<PII> v;
int read() {
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
LL Pow(LL a, LL b){
LL sum = 1;
for(; b; b >>= 1){
if(b & 1) sum = sum * a % MOD;
a = a * a % MOD;
}
return sum;
}
void Get_Inv(){
int t = N - 10;
fac[0] = 1;
for(int i = 1; i <= t; i ++) fac[i] = fac[i - 1] * i % MOD;
inv[t] = Pow(fac[t], MOD - 2);
for(int i = t - 1; i >= 1; i --) inv[i] = inv[i + 1] * (i + 1) % MOD;
}
LL C(int n, int m){
if(n < m) return 0;
if(m == 0 || n == m) return 1;
return fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int main() {
Get_Inv(); n = read();
for(int i = 1; i <= n; i ++) a[i] = b[i] = read();
sort(b + 1, b + n + 1);
t = unique(b + 1, b + n + 1) - (b + 1);
for(int i = 1; i <= n; i ++) {
a[i] = lower_bound(b + 1, b + t + 1, a[i]) - b;
sum[a[i]] ++;
}
for(int i = 1; i <= t; i ++) num[sum[i]] ++;
for(int i = 1; i <= n; i ++)
if(num[i]) v.push_back(MP(i, num[i]));
for(int k = 1; k <= n; k ++) {
LL ans = 0;
for(int i = 0; i < (int) v.size(); i ++)
ans = (ans + v[i].Y * (C(n, k) + MOD - C(n - v[i].X, k)) % MOD) % MOD;
ans = ans * Pow(C(n, k), MOD - 2) % MOD;
printf("%lld\n", ans);
}
return 0;
}