f(i,j) f ( i , j ) 表示最后一次匹配的位置 ≤i ≤ i ,匹配了 j j 个位置的方案数。
#include
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;
}
不难发现砖块掉落的顺序不影响最后答案,枚举空位进行DP转移,那么就是要区间长度和区间内砖块个数相等。
#include
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;
}
对每种颜色求出它的极长链,那么相当于要给颜色定一个拓扑序,用倍增优化连边即可。
#include
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);
vectorint , 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;
}
将每个向量的两个 1 1 连一条边,对于一个连通块,它的贡献是它的大小减 1 1 。
#include
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;
}
预处理每种状态是否合法,枚举开头结尾接起来的那一段是哪种调,然后贪心即可。
#include
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;
}
肯定是先把按照 c c 从大到小的顺序把 1 1 变成 0 0 ,再按照从小到大的顺序把 0 0 变成 1 1 。注意一开始可以将一些 1 1 先变成 0 0 再变成 1 1 ,枚举变了前多少大的计算即可。
#include
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);
vectorint , 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;
}
模拟。
#include
using namespace std;
typedef long long ll;
ll solve(vector > 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 > 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;
}
考虑如果猫和老鼠在相邻的边上了,老鼠先走,那么猫有两种策略:
顺着怼过去。
老鼠走一步,猫停留,老鼠走回来(必须回来不然不优),猫和老鼠走到一个点上,老鼠走一步,猫跟上,老鼠走一步,猫不动(相当于用 4 4 步的代价堵住老鼠要往复的边,老鼠位置不变)。
将每条边 (mouse,cat) ( m o u s e , c a t ) 作为状态预处理到终止态的最短路,然后枚举老鼠走了几步之后猫开始堵住老鼠,可以通过预处理每个点的深度来知道猫是否能到达,之后就用预处理好的最短路来计算答案。
#include
using namespace std;
const int N = 100005;
const int inf = 0x3f3f3f3f;
int n, mouse, dist[N], depth[N];
vectorint , 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;
}
状态不多,记忆化搜索,碰到栈中的元素答案就是 −1 − 1 。
#include
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;
}
打表找规律。
#include
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;
}
考虑 Ai=1 A i = 1 的,显然我们会顺着放 N,N−1,N−2,⋯ N , N − 1 , N − 2 , ⋯ 然后开始放 Ai=2 A i = 2 的。排序即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vectorint , int>> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i].first);
a[i].second = i;
}
sort(a.begin(), a.end(), greaterint, 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;
}
因为总边数是 4(n−1) 4 ( n − 1 ) ,所以一定有一个点度数不超过 3 3 ,所以答案不超过 3 3 。所以有一棵树一定只断了一条边,那么在另一棵树中,如果一条边两端连接的两个点在第一棵树中是不同的连通块,那么这条边就要割掉。用 dsu on tree 统计即可。
#include
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;
}