Concerts
f(i,j) f ( i , j ) 表示最后一次匹配的位置 ≤i ≤ i ,匹配了 j j 个位置的方案数。
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
const int M = 305;
const int mod = 1e9 + 7;
int n, m, wait[M], f[N][M];
char s[N], t[M];
int add(int x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
return x;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d", &m, &n);
for (int i = 0; i < 26; ++i) {
scanf("%d", &wait[i]);
}
scanf("%s%s", t + 1, s + 1);
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
f[i][j] = f[i - 1][j];
if (s[i] == t[j]) {
if (j == 1) {
f[i][j] = add(f[i][j], f[i - 1][j - 1]);
} else {
int k = i - wait[t[j - 1] - 'A'] - 1;
if (k >= 0) {
f[i][j] = add(f[i][j], f[k][j - 1]);
}
}
}
}
}
printf("%d\n", f[n][m]);
return 0;
}
Bricks
不难发现砖块掉落的顺序不影响最后答案,枚举空位进行DP转移,那么就是要区间长度和区间内砖块个数相等。
#include <bits/stdc++.h>
using namespace std;
const int N = 1100005;
const int mod = 1e9 + 7;
int n, m, f[N], g[N], s[N];
int add(int x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
return x;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d", &n, &m);
++n;
while (m--) {
int x;
scanf("%d", &x);
++s[x];
}
for (int i = 1; i <= n; ++i) {
s[i] += s[i - 1];
}
g[n] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i] == s[i - 1]) {
f[i] = g[s[i - 1] - (i - 1) + n];
g[s[i] - i + n] = add(g[s[i] - i + n], f[i]);
}
}
printf("%d\n", f[n]);
return 0;
}
Christmas Tree
对每种颜色求出它的极长链,那么相当于要给颜色定一个拓扑序,用倍增优化连边即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
const int M = 17;
const int S = 2000005;
int n, m, total, color[N], depth[N], answer_x[N], answer_y[N], degree[S], id[M][N], anc[M][N];
vector<int> adj[N], nodes[N], to[S];
void dfs(int x) {
for (int i = 1; i < M; ++i) {
anc[i][x] = anc[i - 1][anc[i - 1][x]];
}
for (auto y : adj[x]) {
if (y != anc[0][x]) {
anc[0][y] = x;
depth[y] = depth[x] + 1;
dfs(y);
}
}
}
int jump(int x, int d) {
for (int i = 0; i < M; ++i) {
if (d >> i & 1) {
x = anc[i][x];
}
}
return x;
}
int lca(int x, int y) {
if (depth[x] > depth[y]) {
x = jump(x, depth[x] - depth[y]);
} else if (depth[x] < depth[y]) {
y = jump(y, depth[y] - depth[x]);
}
if (x == y) {
return x;
}
for (int i = M - 1; ~i; --i) {
if (anc[i][x] != anc[i][y]) {
x = anc[i][x];
y = anc[i][y];
}
}
return anc[0][x];
}
int dist(int x, int y) {
return depth[x] + depth[y] - (depth[lca(x, y)] << 1);
}
int find(int x, int y, int z, int d) {
if (d <= depth[x] - depth[z]) {
return jump(x, d);
} else {
return jump(y, depth[x] + depth[y] - (depth[z] << 1) - d);
}
}
void add_edge(int color, int x, int y) {
if (depth[x] < depth[y]) {
swap(x, y);
}
if (depth[x] > depth[y]) {
for (int i = 0; i < M; ++i) {
if (depth[x] - depth[y] >> i & 1) {
to[color].push_back(id[i][x]);
x = anc[i][x];
}
}
}
if (x == y) {
to[color].push_back(id[0][x]);
return;
}
for (int i = M - 1; ~i; --i) {
if (anc[i][x] != anc[i][y]) {
to[color].push_back(id[i][x]);
to[color].push_back(id[i][y]);
x = anc[i][x];
y = anc[i][y];
}
}
to[color].push_back(id[0][x]);
to[color].push_back(id[0][y]);
to[color].push_back(id[0][anc[0][x]]);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d", &color[i]);
nodes[color[i]].push_back(i);
}
for (int i = 1; i < n; ++i) {
int x, y;
scanf("%d %d", &x, &y);
adj[x].push_back(y);
adj[y].push_back(x);
}
dfs(1);
total = m;
for (int i = 0; i < M; ++i) {
for (int j = 1; j <= n; ++j) {
id[i][j] = ++total;
}
}
for (int i = 1; i <= n; ++i) {
to[id[0][i]].push_back(color[i]);
}
for (int i = 1; i < M; ++i) {
for (int j = 1; j <= n; ++j) {
if (anc[i - 1][j]) {
to[id[i][j]].push_back(id[i - 1][j]);
to[id[i][j]].push_back(id[i - 1][anc[i - 1][j]]);
}
}
}
for (int i = 1; i <= m; ++i) {
if (nodes[i].empty()) {
printf("%d 1 1\n", i);
continue;
}
int x = nodes[i][0], y = nodes[i][0], diameter = 0;
for (int j = 1; j < nodes[i].size(); ++j) {
int z = nodes[i][j], d, type = 0;
d = dist(x, z);
if (d > diameter) {
diameter = d;
type = 1;
}
d = dist(y, z);
if (d > diameter) {
diameter = d;
type = 2;
}
if (type == 1) {
y = z;
} else if (type == 2) {
x = z;
}
}
answer_x[i] = x;
answer_y[i] = y;
int z = lca(x, y);
vector<pair<int, int>> all;
for (auto p : nodes[i]) {
all.push_back(make_pair(dist(x, p), p));
}
sort(all.begin(), all.end());
for (int j = 1; j < all.size(); ++j) {
if (all[j].first != all[j - 1].first + 1) {
int u = find(x, y, z, all[j].first - 1), v = find(x, y, z, all[j - 1].first + 1);
add_edge(i, u, v);
}
}
}
for (int x = 1; x <= total; ++x) {
for (auto y : to[x]) {
++degree[y];
}
}
queue<int> q;
for (int i = 1; i <= total; ++i) {
if (!degree[i]) {
q.push(i);
}
}
while (!q.empty()) {
int x = q.front();
q.pop();
if (x <= m && !nodes[x].empty()) {
printf("%d %d %d\n", x, answer_x[x], answer_y[x]);
}
for (auto y : to[x]) {
if (!--degree[y]) {
q.push(y);
}
}
}
return 0;
}
Harry Potter and The Vector Spell
将每个向量的两个 连一条边,对于一个连通块,它的贡献是它的大小减 1 1 。
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, f[N], size[N];
vector<int> a[N];
int find(int x) {
while (x != f[x]) {
x = f[x] = f[f[x]];
}
return x;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d", &m, &n);
for (int i = 1; i <= m; ++i) {
int k;
scanf("%d", &k);
while (k--) {
int x;
scanf("%d", &x);
a[x].push_back(i);
}
f[i] = i;
}
for (int i = 1; i <= n; ++i) {
f[find(a[i][0])] = find(a[i][1]);
}
for (int i = 1; i <= m; ++i) {
++size[find(i)];
}
int answer = 0;
for (int i = 1; i <= m; ++i) {
if (size[i]) {
answer += size[i] - 1;
}
}
printf("%d\n", answer);
return 0;
}
Looping Playlist
预处理每种状态是否合法,枚举开头结尾接起来的那一段是哪种调,然后贪心即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 10000005;
map<string, int> f;
int n, a[N];
bool ok[N];
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
f["Do"] = 0;
f["Do#"] = 1;
f["Re"] = 2;
f["Re#"] = 3;
f["Mi"] = 4;
f["Fa"] = 5;
f["Fa#"] = 6;
f["Sol"] = 7;
f["Sol#"] = 8;
f["La"] = 9;
f["La#"] = 10;
f["Si"] = 11;
for (int i = 0; i < 12; ++i) {
int s = 0;
s |= 1 << (i + 0) % 12;
s |= 1 << (i + 2) % 12;
s |= 1 << (i + 4) % 12;
s |= 1 << (i + 5) % 12;
s |= 1 << (i + 7) % 12;
s |= 1 << (i + 9) % 12;
s |= 1 << (i + 11) % 12;
ok[s] = true;
}
for (int i = 1; i < 1 << 12; i <<= 1) {
for (int j = 0; j < 1 << 12; j += i << 1) {
for (int k = 0; k < i; ++k) {
ok[j + k] |= ok[j + k + i];
}
}
}
cin >> n;
for (int i = 1; i <= n; ++i) {
string s;
cin >> s;
a[i] = f[s];
}
int answer = n;
for (int i = 0; i < 12; ++i) {
int s = 0;
s |= 1 << (i + 0) % 12;
s |= 1 << (i + 2) % 12;
s |= 1 << (i + 4) % 12;
s |= 1 << (i + 5) % 12;
s |= 1 << (i + 7) % 12;
s |= 1 << (i + 9) % 12;
s |= 1 << (i + 11) % 12;
int l = 1, r = n;
while (l <= n && (s >> a[l] & 1)) {
++l;
}
while (r && (s >> a[r] & 1)) {
--r;
}
int result = 1, state = (1 << 12) - 1;
for (int j = l; j <= r; ++j) {
if (!ok[state | 1 << a[j]]) {
state = 1 << a[j];
++result;
} else {
state |= 1 << a[j];
}
}
answer = min(answer, result);
}
printf("%d\n", answer);
return 0;
}
Binary Transformations
肯定是先把按照 从大到小的顺序把 1 1 变成 ,再按照从小到大的顺序把 0 0 变成 。注意一开始可以将一些 1 1 先变成 再变成 1 1 ,枚举变了前多少大的计算即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5005;
pair<int, int> all[N];
char s[N], t[N];
int n, a[N];
bool use[N];
ll solve() {
ll result = 0, sum = 0;
for (int i = 1; i <= n; ++i) {
if (s[i] == '1') {
sum += a[i];
}
}
for (int i = n; i; --i) {
int x = all[i].second;
if (use[x] || (s[x] == '1' && t[x] == '0')) {
sum -= a[x];
result += sum;
}
}
for (int i = 1; i <= n; ++i) {
int x = all[i].second;
if (use[x] || (s[x] == '0' && t[x] == '1')) {
sum += a[x];
result += sum;
}
}
return result;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
all[i] = make_pair(a[i], i);
}
sort(all + 1, all + n + 1);
scanf("%s %s", s + 1, t + 1);
vector<pair<int, int>> to_change;
for (int i = 1; i <= n; ++i) {
if (s[i] == '1' && t[i] == '1') {
to_change.push_back(make_pair(-a[i], i));
}
}
sort(to_change.begin(), to_change.end());
ll answer = solve();
for (auto p : to_change) {
use[p.second] = true;
answer = min(answer, solve());
}
printf("%lld\n", answer);
return 0;
}
Robots
模拟。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll solve(vector<pair<ll, ll>> a) {
ll v = 0, result = 0;
for (auto p : a) {
result += 2ll * v * p.second + p.first * p.second * p.second;
v += p.first * p.second;
}
return result;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<pair<ll, ll>> a(n);
for (int i = 0; i < n; ++i) {
scanf("%lld %lld", &a[i].first, &a[i].second);
}
ll answer = -solve(a);
sort(a.begin(), a.end());
reverse(a.begin(), a.end());
answer += solve(a);
printf("%lld.%d\n", answer >> 1, answer & 1 ? 5 : 0);
return 0;
}
Cat and Mouse
考虑如果猫和老鼠在相邻的边上了,老鼠先走,那么猫有两种策略:
顺着怼过去。
老鼠走一步,猫停留,老鼠走回来(必须回来不然不优),猫和老鼠走到一个点上,老鼠走一步,猫跟上,老鼠走一步,猫不动(相当于用 步的代价堵住老鼠要往复的边,老鼠位置不变)。
将每条边 (mouse,cat) ( m o u s e , c a t ) 作为状态预处理到终止态的最短路,然后枚举老鼠走了几步之后猫开始堵住老鼠,可以通过预处理每个点的深度来知道猫是否能到达,之后就用预处理好的最短路来计算答案。
#include <bits/stdc++.h> using namespace std; const int N = 100005; const int inf = 0x3f3f3f3f; int n, mouse, dist[N], depth[N]; vector<pair<int, int>> edges, to[N]; vector<int> adj[N], pool[N << 2]; void dfs(int x, int parent) { for (auto y : adj[x]) { if (y != parent) { depth[y] = depth[x] + 1; dfs(y, x); } } } int get_id(int x, int y) { return lower_bound(edges.begin(), edges.end(), make_pair(x, y)) - edges.begin(); } int find_next(int x, int ban) { for (auto y : adj[x]) { if (y != ban) { return y; } } return -1; } int main() { #ifdef wxh010910 freopen("input.txt", "r", stdin); #endif int T; scanf("%d", &T); while (T--) { scanf("%d %d", &n, &mouse); --mouse; for (int i = 0; i < n; ++i) { adj[i].clear(); } edges.clear(); for (int i = 0; i < n - 1; ++i) { int x, y; scanf("%d %d", &x, &y); --x; --y; adj[x].push_back(y); adj[y].push_back(x); edges.push_back(make_pair(x, y)); edges.push_back(make_pair(y, x)); } sort(edges.begin(), edges.end()); for (int i = 0; i < n; ++i) { reverse(adj[i].begin(), adj[i].end()); } if (!~find_next(mouse, -1)) { puts("0"); continue; } dfs(0, -1); for (int i = 0; i < n << 2; ++i) { pool[i].clear(); } for (int i = 0; i < edges.size(); ++i) { dist[i] = inf; to[i].clear(); } for (int i = 0; i < edges.size(); ++i) { int x = edges[i].first, y = edges[i].second, z = find_next(x, y); if (!~z) { dist[i] = 0; pool[0].push_back(i); } else { to[get_id(z, x)].push_back(make_pair(i, 1)); if (find_next(z, -1) == x) { to[get_id(x, z)].push_back(make_pair(i, 4)); } } } for (int i = 0; i < n << 2; ++i) { for (auto x : pool[i]) { if (dist[x] == i) { for (auto e : to[x]) { if (dist[e.first] > i + e.second) { dist[e.first] = i + e.second; pool[i + e.second].push_back(e.first); } } } } } int answer = inf, current = mouse; for (int i = 0; i < n; ++i) { int next = find_next(current, i ? -1 : 0); if (depth[next] < i || (depth[next] == i && depth[current] != i - 1)) { answer = min(answer, i + dist[get_id(current, next)]); } current = next; } printf("%d\n", answer); } return 0; }
Tetris
状态不多,记忆化搜索,碰到栈中的元素答案就是 −1 − 1 。
#include <bits/stdc++.h> using namespace std; const int N = 55; const char s[3][N] = { "010111110011110010010010011001", "111100011010010010111011111111", "010100001110011111011110110110" }; map<int, int> mem[N], visit[N]; int n, a[N]; int go(int state, int type, int rotate) { if (state >> 21) { return -1; } char b[3][3]; if (!rotate) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { b[i][j] = s[i][j + type * 3]; } } } else if (rotate == 1) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { b[i][j] = s[j][2 - i + type * 3]; } } } else if (rotate == 2) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { b[i][j] = s[2 - i][2 - j + type * 3]; } } } else { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { b[i][j] = s[2 - j][i + type * 3]; } } } int p = 7; for (int i = 6; ~i; --i) { bool flag = false; for (int j = 0; j < 3; ++j) { for (int k = 0; k < 3; ++k) { if (b[j][k] == '1' && (state >> (i + j) * 3 + k & 1)) { flag = true; } } } if (flag) { break; } p = i; } int new_state = state; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (b[i][j] == '1') { new_state |= 1 << (i + p) * 3 + j; } } } for (int i = 0; i < 10; ++i) { while ((new_state >> i * 3 & 7) == 7) { new_state = (new_state & (1 << 3 * i) - 1) | (new_state >> (i + 1) * 3 << i * 3); } } return new_state; } int dfs(int state, int x) { if (visit[x][state] == 1) { return -1; } if (visit[x][state] == 2) { return mem[x][state]; } visit[x][state] = 1; int result = 0; for (int i = 0; i < 4; ++i) { int next = go(state, a[x], i); if (~next) { int value = dfs(next, (x + 1) % n); if (!~value) { return -1; } result = max(result, value + 1); } } visit[x][state] = 2; mem[x][state] = result; return result; } int main() { #ifdef wxh010910 freopen("input.txt", "r", stdin); #endif scanf("%d", &n); for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); } printf("%d\n", dfs(0, 0)); return 0; }
Cunning Friends
打表找规律。
#include <bits/stdc++.h> using namespace std; int main() { #ifdef wxh010910 freopen("input.txt", "r", stdin); #endif int n, one = 0, two = 0, three = 0; scanf("%d", &n); if (n == 1) { puts("Win"); return 0; } while (n--) { int x; scanf("%d", &x); if (x == 1) { ++one; } else if (x == 2) { ++two; } else { ++three; } } if (three > 1 || two > 2 || three + two == 3) { puts("Lose"); return 0; } if (three + two == 1 || one % 3) { puts("Win"); } else { puts("Lose"); } return 0; }
Escape Room
考虑 Ai=1 A i = 1 的,显然我们会顺着放 N,N−1,N−2,⋯ N , N − 1 , N − 2 , ⋯ 然后开始放 Ai=2 A i = 2 的。排序即可。
#include <bits/stdc++.h> using namespace std; int main() { #ifdef wxh010910 freopen("input.txt", "r", stdin); #endif int n; scanf("%d", &n); vector<pair<int, int>> a(n); for (int i = 0; i < n; ++i) { scanf("%d", &a[i].first); a[i].second = i; } sort(a.begin(), a.end(), greater<pair<int, int>> ()); vector<int> answer(n); for (int i = 0; i < n; ++i) { answer[a[i].second] = i; } for (int i = 0; i < n; ++i) { printf("%d%c", answer[i] + 1, i == n - 1 ? '\n' : ' '); } return 0; }
Divide and Conquer
因为总边数是 4(n−1) 4 ( n − 1 ) ,所以一定有一个点度数不超过 3 3 ,所以答案不超过 。所以有一棵树一定只断了一条边,那么在另一棵树中,如果一条边两端连接的两个点在第一棵树中是不同的连通块,那么这条边就要割掉。用 dsu on tree 统计即可。
#include <bits/stdc++.h> using namespace std; const int N = 100005; pair<int, int> result; int n, current; bool color[N]; struct tree_t { int son[N], size[N]; vector<int> adj[N]; void dfs(int x, int parent) { size[x] = 1; for (auto y : adj[x]) { if (y != parent) { dfs(y, x); size[x] += size[y]; if (size[y] > size[son[x]]) { son[x] = y; } } } } } a, b; void flip(int x, tree_t &a) { for (auto y : a.adj[x]) { if (color[y] == color[x]) { ++current; } else { --current; } } color[x] = !color[x]; } void modify(int x, int parent, tree_t &a, tree_t &b) { flip(x, b); for (auto y : a.adj[x]) { if (y != parent) { modify(y, x, a, b); } } } void dfs(int x, int parent, bool keep, tree_t &a, tree_t &b) { for (auto y : a.adj[x]) { if (y != parent && y != a.son[x]) { dfs(y, x, false, a, b); } } if (a.son[x]) { dfs(a.son[x], x, true, a, b); } for (auto y : a.adj[x]) { if (y != parent && y != a.son[x]) { modify(y, x, a, b); } } flip(x, b); if (parent) { if (current < result.first) { result = make_pair(current, 0); } if (current == result.first) { ++result.second; } } if (!keep) { modify(x, parent, a, b); } } pair<int, int> solve(tree_t &a, tree_t &b) { a.dfs(1, 0); result = make_pair(n, 0); dfs(1, 0, false, a, b); ++result.first; } int main() { #ifdef wxh010910 freopen("input.txt", "r", stdin); #endif scanf("%d", &n); for (int i = 1; i < n; ++i) { int x, y; scanf("%d %d", &x, &y); a.adj[x].push_back(y); a.adj[y].push_back(x); } for (int i = 1; i < n; ++i) { int x, y; scanf("%d %d", &x, &y); b.adj[x].push_back(y); b.adj[y].push_back(x); } solve(a, b); if (result.first == 2) { printf("2 %d\n", result.second); return 0; } int answer = 0; if (result.first == 3) { answer += result.second; } solve(b, a); if (result.first == 3) { answer += result.second; } printf("3 %d\n", answer); return 0; }