AGC002D——F
阿新 • • 發佈:2020-12-24
D
多次詢問,整體二分。具體一些,當前二分的區間為 \(l,r\),先設定答案為 \(mid\),判斷是否可行,可行的往 \([l,mid]\) 遞迴,否則走 \([mid + 1, r]\)。關鍵在於如何快速判定是否可行。
用並查集維護連通塊的大小,一種辦法使用可回退並查集。還有一種是按照 \(bfs\) 順序處理區間(按層處理)。這樣只要在每層開頭暴力清空,然後按照順序連
#include <bits/stdc++.h> using namespace std; void read (int &x) { char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); } const int N = 1e5 + 5; int n, m, q, res[N]; struct query { int x, y, z; } a[N]; struct edge { int x, y; } e[N]; int fa[N], sz[N]; int find (int x) { return fa[x] == x ? x : fa[x] = find (fa[x]); } void merge (int x, int y) { int fx = find (x), fy = find (y); if (sz[fx] > sz[fy]) swap (fx, fy); if (fx ^ fy) fa[fx] = fy, sz[fy] += sz[fx]; } int cnt, ll[N << 2], rr[N << 2], tag[N << 2]; vector<int> g[N << 2]; #define ls (p << 1) #define rs (p << 1 | 1) void build (int p, int l, int r) { ll[p] = l, rr[p] = r, tag[p] = 1; if (l == r) return; int mid (l + r >> 1); build (ls, l, mid), build (rs, mid + 1, r); } void solve () { for (int p = 1; p <= (n << 2); ++p) { if (!tag[p]) continue; // printf ("%d %d\n", ll[p], rr[p]); if (ll[p] == 1) { for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1; } if (ll[p] == rr[p]) { merge (e[ll[p]].x, e[ll[p]].y); for (int i : g[p]) res[i] = ll[p]; continue; } int mid (ll[p] + rr[p] >> 1); for (int i = ll[p]; i <= mid; ++i) merge (e[i].x, e[i].y); for (int i : g[p]) { int x = a[i].x, y = a[i].y, z = a[i].z; x = find (x), y = find (y); int s = (x == y) ? sz[x] : sz[x] + sz[y]; // printf ("%d %d %d\n", i, s, z); s >= z ? g[ls].push_back (i) : g[rs].push_back (i); } for (int i = mid + 1; i <= rr[p]; ++i) merge (e[i].x, e[i].y); } } signed main() { read (n), read (m); for (int i = 1; i <= m; ++i) read (e[i].x), read (e[i].y); read (q); for (int i = 1; i <= q; ++i) read (a[i].x), read (a[i].y), read (a[i].z); for (int i = 1; i <= q; ++i) g[1].push_back (i); build (1, 1, m); solve (); for (int i = 1; i <= q; ++i) printf ("%d\n", res[i]); return 0; }
E
剽三張圖助於理解。把所有石堆排序扔到座標軸上後事情就很明顯了。兩種操作分別代表向上走一步和向右走一步。最外圍一圈的勝負狀態確定。
可以發現,先手可以走到任意的 \((x,y),|x-y|\leq1\),後手可以控制 \(|x-y|\leq1\),所以只要判斷是否存在這樣的 \((x,y)\) 讓先手勝,不勝則敗。
#include <bits/stdc++.h> using namespace std; #define int long long void read (int &x) { char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); } const int N = 1e5 + 5; int n, tag, a[N]; void work () { for (int i = 1; i <= n; ++i) { int j = i + 1; while (a[i] == a[j] && j <= n) ++j; --j; int l = i, r = j; if ((r - a[i]) & 1) { if (l <= a[i] - 1 && a[i] - 1 <= r) { tag = 1; break; } if (l <= a[i] + 1 && a[i] + 1 <= r) { tag = 1; break; } } i = j; l = a[i + 1] + 1, r = a[i]; if (((r - i) & 1)) { if (l <= i - 1 && i - 1 <= r) { tag = 1; break; } if (l <= i + 1 && i + 1 <= r) { tag = 1; break; } } } } signed main() { read (n); for (int i = 1; i <= n; ++i) read (a[i]); sort (a + 1, a + n + 1), reverse (a + 1, a + n + 1); work (); puts (tag ? "First" : "Second"); return 0; }
F
因為每種顏色的求會有一個白色,所以可以重新把球分類為白球和其他顏色,這兩種球相對獨立
這就是狀態了:\(f_{i,j}\) 表示當前填了 \(i\) 個白球,\(j\) 類其他顏色的球的方案數 (其他顏色都是一下全部填完)
轉移依據也比較奇特:按照當前空位最左邊填什麼轉移。這樣可以保證白球的合法性
如果填白的直接轉,否則乘上一個組合數。當然其他顏色種類不能大於白球數量。組合數具體是什麼不難想,就不寫了(懶)
#include <bits/stdc++.h> using namespace std; #define int long long void read (int &x) { char ch = getchar(); x = 0; while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar(); } const int N = 2010, mod = 1e9 + 7; int n, k, f[N][N], pw[N * N], in[N * N]; int qpow (int x, int y) { int t = 1; while (y) { if (y & 1) t = t * x % mod; x = x * x % mod, y >>= 1; } return t; } int C (int x, int y) { // if (x < y) return 0; return pw[x] * in[y] % mod * in[x - y] % mod; } signed main() { read (n), read (k); f[0][0] = pw[0] = 1; for (int i = 1; i <= n * k; ++i) pw[i] = pw[i - 1] * i % mod; in[n * k] = qpow (pw[n * k], mod - 2); for (int i = n * k; i >= 1; --i) in[i - 1] = in[i] * i % mod; if (k == 1) return puts ("1"), 0; for (int i = 1; i <= n; ++i) for (int j = 0; j <= i; ++j) { f[i][j] = f[i - 1][j]; if (j) (f[i][j] += f[i][j - 1] * (n - j + 1) % mod * C (n * k - i - (j - 1) * (k - 1) - 1, k - 2)) %= mod; } return printf ("%lld\n", f[n][n]), 0; }