AtCoder Grand Contest 002 DEF
D - Stamp Rally
多次询问,整体二分。具体一些,当前二分的区间为 \(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 - Candy Piles
剽三张图助于理解。把所有石堆排序扔到坐标轴上后事情就很明显了。两种操作分别代表向上走一步和向右走一步。最外围一圈的胜负状态确定。
可以发现,先手可以走到任意的 \((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 - Leftmost Ball
因为每种颜色的求会有一个白色,所以可以重新把球分类为白球和其他颜色,这两种球相对独立
这就是状态了:\(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;
}