特判 n = 0 n=0 n=0 或者 k = 1 k=1 k=1 的情况,当 k > 1 k>1 k>1 的时候,先手取中间,然后后手不管取什么先手取相对的,所以先手必胜。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
scanf("%d %d", &n, &m);
if (m == 1) {
puts(n & 1 ? "Adrien" : "Austin");
} else {
puts(n ? "Adrien" : "Austin");
}
return 0;
}
凸优化+决策单调性DP。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
scanf("%d %d", &n, &m);
vector<long long> sum(n + 1);
vector<long long> f(n + 1);
vector<int> g(n + 1);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &sum[i]);
sum[i] += sum[i - 1];
}
vector<pair<int, int>> q(n);
auto solve = [&](long long cost) {
int ll = 0, rr = 0;
auto get = [&](int l, int r) {
return f[l] + cost + sum[l] + sum[r] - sum[l + r >> 1] - sum[l + r + 1 >> 1];
};
for (int i = 0; i < n; ++i) {
while (ll < rr && get(i, q[rr - 1].second) < get(q[rr - 1].first, q[rr - 1].second)) {
--rr;
}
if (ll == rr) {
q[rr++] = make_pair(i, i + 1);
} else {
int l = q[rr - 1].second, r = n + 1;
while (l < r) {
int mid = l + r >> 1;
if (get(i, mid) < get(q[rr - 1].first, mid)) {
r = mid;
} else {
l = mid + 1;
}
}
if (r <= n) {
q[rr++] = make_pair(i, r);
}
}
while (ll + 1 < rr && q[ll + 1].second <= i + 1) {
++ll;
}
f[i + 1] = get(q[ll].first, i + 1);
g[i + 1] = g[q[ll].first] + 1;
}
};
long long l = 0, r = sum[n];
while (l < r) {
long long mid = l + r >> 1;
solve(mid);
if (g[n] <= m) {
r = mid;
} else {
l = mid + 1;
}
}
solve(r);
printf("%lld\n", f[n] - r * m);
return 0;
}
考虑第一个人选了一个点之后,第二个人一定会选择某个子树的重心,然后第一个人删去最大的那个子树。这时候我们求出了一个最优解,假设第一个人选的点不在第二个人当前这个解所在子树内,那么第二个人保持同样的决策答案不会对第一个人更优。每次选重心之后朝一个子树递归即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<vector<int>> adj(n);
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);
}
int answer = 0;
vector<int> size(n), parent(n);
vector<bool> visit(n);
auto find_centroid = [&](int x, bool ban) {
vector<int> order(1, x);
for (int i = 0; i < order.size(); ++i) {
int x = order[i];
for (auto y : adj[x]) {
if ((!visit[y] || !ban) && y != parent[x]) {
order.push_back(y);
parent[y] = x;
}
}
}
int root = -1, value = n;
for (int i = order.size() - 1; ~i; --i) {
int x = order[i], subtree = 0;
size[x] = 1;
for (auto y : adj[x]) {
if ((!visit[y] || !ban) && y != parent[x]) {
size[x] += size[y];
subtree = max(subtree, size[y]);
}
}
subtree = max(subtree, (int)order.size() - size[x]);
if (subtree < value) {
root = x;
value = subtree;
}
}
return make_pair(root, value + n - (int)order.size());
};
auto get = [&](int x) {
pair<int, int> result = make_pair(n, -1);
for (auto y : adj[x]) {
parent[y] = x;
result = min(result, make_pair(find_centroid(y, false).second, y));
}
return result;
};
int x = 0;
while (!visit[x]) {
parent[x] = -1;
x = find_centroid(x, true).first;
visit[x] = true;
pair<int, int> result = get(x);
answer = max(answer, result.first);
x = result.second;
}
printf("%d\n", answer);
return 0;
}
三分套三分套三分。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<vector<int>> a(3, vector<int> (n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < 3; ++j) {
scanf("%d", &a[j][i]);
}
}
vector<double> p(3);
function<double(int)> ternary_search = [&](int x) {
if (x == 3) {
double answer = 0;
for (int i = 0; i < n; ++i) {
double result = 0;
for (int j = 0; j < 3; ++j) {
result += (p[j] - a[j][i]) * (p[j] - a[j][i]);
}
answer = max(answer, result);
}
return answer;
} else {
double l = *min_element(a[x].begin(), a[x].end());
double r = *max_element(a[x].begin(), a[x].end());
double answer = 1e18;
for (int tt = 0; tt < 50; ++tt) {
double ll = l + (r - l) / 3, rr = r - (r - l) / 3, result_l, result_r;
p[x] = ll;
result_l = ternary_search(x + 1);
p[x] = rr;
result_r = ternary_search(x + 1);
answer = min(answer, min(result_l, result_r));
if (result_l < result_r) {
r = rr;
} else {
l = ll;
}
}
return answer;
}
};
printf("%lf\n", sqrt(ternary_search(0)));
return 0;
}
注意到可以进行一个操作就是把一个 1 1 1 往前推 k k k 步,然后如果有连续的 k k k 个 1 1 1 就消掉,搞个最小表示即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
string s, t;
cin >> s >> t;
auto get = [&](string s) {
vector<pair<int, int>> p(1, make_pair(-1, 0));
for (int i = 0; i < n; ++i) {
if (s[i] == '1') {
int x = i % m + (p.back().first + 1) / m * m;
if (x <= p.back().first) {
x += m;
}
p.emplace_back(x, x == p.back().first + 1 ? p.back().second + 1 : 1);
if (p.back().second == m) {
for (int j = 0; j < m; ++j) {
p.pop_back();
}
}
}
}
string result(n, '0');
for (int i = 1; i < p.size(); ++i) {
result[p[i].first] = '1';
}
return result;
};
cout << (get(s) == get(t) ? "Yes" : "No") << endl;
return 0;
}
记 f ( i , j ) f(i,j) f(i,j) 表示从 i i i 走到 j j j 的期望步数, g ( i ) g(i) g(i) 表示 i i i 走一圈回到 i i i 的期望步数, e ( i , j ) e(i,j) e(i,j) 表示从 i i i 走一步走到 j j j 的概率,那么 f ( i , j ) = ∑ k e ( i , k ) f ( k , j ) + 1 − [ i = j ] g ( i ) f(i,j) = \sum_{k} e(i,k)f(k,j) + 1 - [i=j]g(i) f(i,j)=∑ke(i,k)f(k,j)+1−[i=j]g(i) 。注意到 g ( i ) = 1 P ( i ) g(i) = \frac{1}{P(i)} g(i)=P(i)1 ,其中 P ( i ) P(i) P(i) 表示无限时刻后停留在 i i i 的概率,这个可以通过高消求出。那么现在就是解一个方程 A X = B AX = B AX=B ,由于 A A A 的秩为 n − 1 n-1 n−1 ,不能直接解,但可以发现 X i , i = 0 X_{i,i} = 0 Xi,i=0 ,可以求出一组特解之后用 X i , i X_{i,i} Xi,i 来调整。
#include
using namespace std;
typedef double ld;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m, q;
scanf("%d %d %d", &n, &m, &q);
vector<vector<ld>> adj(n, vector<ld> (n));
vector<int> degree(n);
while (m--) {
int x, y;
scanf("%d %d", &x, &y);
++adj[x][y];
++degree[x];
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
adj[i][j] /= degree[i];
}
}
vector<vector<ld>> a(n, vector<ld> (n + 1));
for (int i = 0; i <= n; ++i) {
a[0][i] = 1;
}
for (int i = 1; i < n; ++i) {
a[i][i] = 1;
for (int j = 0; j < n; ++j) {
a[i][j] -= adj[j][i];
}
}
for (int i = 0; i < n; ++i) {
int j = i;
while (j < n && !a[j][i]) {
++j;
}
if (j != i) {
for (int k = i; k <= n; ++k) {
swap(a[i][k], a[j][k]);
}
}
for (int j = 0; j < n; ++j) {
if (j != i && a[j][i]) {
ld coef = a[j][i] / a[i][i];
for (int k = i; k <= n; ++k) {
a[j][k] -= a[i][k] * coef;
}
}
}
}
vector<ld> g(n);
for (int i = 0; i < n; ++i) {
g[i] = a[i][i] / a[i][n];
}
a = vector<vector<ld>> (n, vector<ld> (n << 1));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
a[i][j] = (i == j) - adj[i][j];
a[i][j + n] = 1;
}
a[i][i + n] -= g[i];
}
for (int i = 0; i < n - 1; ++i) {
int j = i;
while (j < n && !a[j][i]) {
++j;
}
if (j != i) {
for (int k = i; k < n << 1; ++k) {
swap(a[i][k], a[j][k]);
}
}
ld coef = a[i][i];
for (int j = i; j < n << 1; ++j) {
a[i][j] /= coef;
}
for (int j = 0; j < n; ++j) {
if (j != i && a[j][i]) {
ld coef = a[j][i];
for (int k = i; k < n << 1; ++k) {
a[j][k] -= a[i][k] * coef;
}
}
}
}
vector<vector<ld>> f(n, vector<ld> (n));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
f[i][j] = a[i][j + n] - a[j][j + n];
}
}
while (q--) {
int total, current;
scanf("%d %d", &total, ¤t);
if (total == 1) {
puts("1");
} else {
ld answer = 0;
for (int i = 0; i < total - 1; ++i) {
int next;
scanf("%d", &next);
answer += f[current][next];
current = next;
}
printf("%.9lf\n", answer);
}
}
return 0;
}
枚举每个三角形的bounding-box,相当于每个边界与外边界平行的三角形有边长的贡献,化简一下式子,答案是 ( n + 3 4 ) \binom{n+3}{4} (4n+3) 。
#include
using namespace std;
const int md = 1e9 + 7;
inline int mul(int x, int y) {
return (long long)x * y % md;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
while (tt--) {
int n;
scanf("%d", &n);
printf("%d\n", mul(mul(mul(mul(n, n + 1), n + 2), n + 3), (md + 1) / 24));
}
return 0;
}
如果区间内众数出现次数大于区间长度的一半,那么可以把其他数全消掉;如果区间长度为偶数可以全消掉;否则会剩下一个数,枚举剩下的数是什么,限制大概是个偏序关系,树状数组维护即可。
#include
using namespace std;
template<typename T> class fenwick_t {
public:
vector<T> fenw;
int n;
fenwick_t(int n):n(n) {
fenw.resize(n);
}
void modify(int x, T value) {
while (x < n) {
fenw[x] += value;
x |= x + 1;
}
}
T query(int x) {
T result{};
while (x >= 0) {
result += fenw[x];
x = (x & x + 1) - 1;
}
return result;
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
string s;
cin >> n >> s;
vector<vector<int>> sum(3, vector<int> (n + 1));
vector<vector<fenwick_t<int>>> fenwick(3, vector<fenwick_t<int>> (3, n));
vector<int> max_value(3), available(3);
for (int i = 0; i < 3; ++i) {
for (int j = n - 1; ~j; --j) {
sum[i][j] = sum[i][j + 1] + (s[j] == i + '0' ? 1 : -1);
}
max_value[i] = *max_element(sum[i].begin(), sum[i].begin() + n);
}
vector<int> tag(n + 1);
for (int i = n - 1; ~i; --i) {
bool already = false;
for (int j = 0; j < 3; ++j) {
if (sum[j][i] > 0) {
tag[0] += j;
tag[sum[j][i]] -= j;
already = true;
}
}
if (n - i & 1) {
bool can = true;
for (int j = 0; j < 3; ++j) {
if (sum[j][i + 1] > 0) {
can = false;
}
}
if (can) {
++available[s[i] - '0'];
for (int j = 0; j < 3; ++j) {
fenwick[s[i] - '0'][j].modify(max_value[j] - sum[j][i], 1);
}
}
if (!already) {
for (int j = 0; j < 3; ++j) {
int cnt = 0;
for (int k = 0; k < 3; ++k) {
cnt += fenwick[j][k].query(max_value[k] - sum[k][i]);
}
if (cnt != available[j]) {
tag[0] += j;
tag[1] -= j;
break;
}
}
}
}
}
for (int i = 1; i <= n; ++i) {
tag[i] += tag[i - 1];
}
for (int i = 0; i < tag.size(); ++i) {
if (tag[i] >= 10) {
if (i == tag.size() - 1) {
tag.push_back(0);
}
tag[i + 1] += tag[i] / 10;
tag[i] %= 10;
}
}
while (!tag.empty() && !tag.back()) {
tag.pop_back();
}
if (tag.empty()) {
puts("0");
} else {
for (int i = tag.size() - 1; ~i; --i) {
putchar(tag[i] + '0');
}
putchar(10);
}
return 0;
}
网络流。
#include
using namespace std;
const int inf = 0x3f3f3f3f;
namespace flow {
struct edge_t {
int to, cap, rev;
edge_t(int t, int c, int r) {
to = t;
cap = c;
rev = r;
}
};
int n, source, sink, answer;
vector<vector<edge_t>> adj;
vector<int> dist, current;
void init(int v, int s, int t) {
n = v;
source = s;
sink = t;
answer = 0;
adj.clear();
adj.resize(n);
dist.resize(n);
current.resize(n);
}
void add(int x, int y, int c) {
adj[x].push_back(edge_t(y, c, adj[y].size()));
adj[y].push_back(edge_t(x, 0, adj[x].size() - 1));
}
bool bfs() {
queue<int> q;
for (int i = 0; i < n; ++i) {
dist[i] = -1;
}
dist[source] = 0;
q.push(source);
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto e : adj[x]) {
if (e.cap && !~dist[e.to]) {
dist[e.to] = dist[x] + 1;
if (e.to == sink) {
return true;
}
q.push(e.to);
}
}
}
return false;
}
int dfs(int x, int f) {
if (x == sink) {
return f;
}
for (int &i = current[x]; ~i; --i) {
edge_t &e = adj[x][i];
if (e.cap && dist[e.to] == dist[x] + 1) {
int w = dfs(e.to, min(e.cap, f));
if (w) {
e.cap -= w;
adj[e.to][e.rev].cap += w;
return w;
}
}
}
return 0;
}
int max_flow() {
while (bfs()) {
for (int i = 0; i < n; ++i) {
current[i] = adj[i].size() - 1;
}
while (true) {
int flow = dfs(source, inf);
if (!flow) {
break;
}
answer += flow;
}
}
return answer;
}
}
using flow::source;
using flow::sink;
using flow::init;
using flow::add;
using flow::max_flow;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m, q;
scanf("%d %d %d", &n, &m, &q);
init(n + m + 3, n + m + 1, n + m + 2);
add(source, n + m, q);
for (int i = 0; i < n; ++i) {
add(source, i, 1);
add(n + m, i, 1);
}
for (int i = 0; i < m; ++i) {
add(i + n, sink, 1);
}
for (int i = 0; i < n; ++i) {
int size;
scanf("%d", &size);
while (size--) {
int x;
scanf("%d", &x);
add(i, x - 1 + n, 1);
}
}
printf("%d\n", max_flow());
return 0;
}
对每个质数求出上一次出现的位置,然后随便算算答案就行了。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
int m = *max_element(a.begin(), a.end());
vector<int> minp(m + 1);
vector<int> primes;
for (int i = 2; i <= m; ++i) {
if (!minp[i]) {
minp[i] = i;
primes.push_back(i);
}
for (auto p : primes) {
if (i * p > m) {
break;
}
minp[i * p] = p;
if (i % p == 0) {
break;
}
}
}
vector<int> last(m + 1, -1);
long long answer = 0;
for (int i = 0; i < n; ++i) {
for (int x = a[i]; x != 1; ) {
int p = minp[x];
while (x % p == 0) {
x /= p;
}
answer += (long long)(n - i) * (i - last[p]);
last[p] = i;
}
}
printf("%lld\n", answer);
return 0;
}
每次合并两个即可。
#include
using namespace std;
const vector<int> dx = {-1, 1, 0, 0};
const vector<int> dy = {0, 0, -1, 1};
const string direction = "UDLR";
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
cin >> n >> m;
vector<string> board(n);
for (int i = 0; i < n; ++i) {
cin >> board[i];
}
vector<pair<int, int>> all;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
if (board[i][j] == '1') {
all.emplace_back(i, j);
}
}
}
auto bfs = [&](pair<int, int> from, pair<int, int> to) {
vector<vector<int>> prev(n, vector<int> (m, -1));
queue<pair<int, int>> q;
prev[from.first][from.second] = 4;
q.push(from);
while (!q.empty()) {
int x = q.front().first, y = q.front().second;
q.pop();
for (int i = 0; i < 4; ++i) {
int xx = x + dx[i], yy = y + dy[i];
if (xx >= 0 && xx < n && yy >= 0 && yy < m && board[xx][yy] == '1' && !~prev[xx][yy]) {
prev[xx][yy] = i ^ 1;
q.emplace(xx, yy);
}
}
}
return prev[to.first][to.second];
};
for (int i = 0; i < all.size(); ++i) {
while (all[i] != all[0]) {
int d = bfs(all[i], all[0]);
putchar(direction[d]);
for (auto &p : all) {
int x = p.first + dx[d], y = p.second + dy[d];
if (x >= 0 && x < n && y >= 0 && y < m && board[x][y] == '1') {
p = make_pair(x, y);
}
}
}
}
putchar(10);
return 0;
}
特判掉一些情况之后,假设最后会保留一个 X X X ,保留一个 Y Y Y ,记 d p ( i , j , s , l a s t ) dp(i,j,s,last) dp(i,j,s,last) 表示考虑前 i i i 个数,当前取出的不是 X X X 也不是 Y Y Y 的数减去 X Y XY XY 相邻的对数为 j j j ,是否保留了 X X X 和 Y Y Y ,以及上一个在序列中的数,可以 O ( 1 ) O(1) O(1) 转移。
#include
using namespace std;
const int inf = 0x3f3f3f3f;
template<typename T> inline void cmin(T &x, const T &y) {
x = min(x, y);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, x, y;
scanf("%d %d %d", &n, &x, &y);
vector<int> a(n), c(3);
for (int i = 0; i < n; ++i) {
int z;
scanf("%d", &z);
if (z == x) {
a[i] = 0;
} else if (z == y) {
a[i] = 1;
} else {
a[i] = 2;
}
++c[a[i]];
}
if (!c[0] || !c[1]) {
puts("0");
return 0;
}
if (!c[2]) {
puts("-1");
return 0;
}
int answer = n;
for (int remove = 0; remove < 2; ++remove) {
bool flag = a[0] == 2 || a[n - 1] == 2;
int last = 2;
for (int i = 0; i < n; ++i) {
if (a[i] != remove) {
if (a[i] == 2 && last == 2) {
flag = true;
}
last = a[i];
}
}
if (last == 2) {
flag = true;
}
if (flag) {
answer = min(answer, c[remove]);
} else {
answer = min(answer, c[remove] + 1);
}
}
vector<vector<int>> f(n + 1, vector<int> (12, inf));
f[c[0] + c[1]][2] = 0;
for (int i = 0; i < n; ++i) {
vector<vector<int>> g(n + 1, vector<int> (12, inf));
for (int j = 0; j <= n; ++j) {
for (int state = 0; state < 4; ++state) {
for (int last = 0; last < 3; ++last) {
if (f[j][state * 3 + last] != inf) {
if (a[i] == 2) {
cmin(g[j + 1][state * 3 + last], f[j][state * 3 + last] + 1);
cmin(g[j][state * 3 + 2], f[j][state * 3 + last]);
} else {
cmin(g[j][state * 3 + last], f[j][state * 3 + last] + 1);
cmin(g[j - (last + a[i] == 1)][(state | 1 << a[i]) * 3 + a[i]], f[j][state * 3 + last]);
}
}
}
}
}
f = g;
}
for (int i = c[0] + c[1]; i <= n; ++i) {
for (int j = 9; j < 12; ++j) {
answer = min(answer, f[i][j]);
}
}
printf("%d\n", answer);
return 0;
}
将 s s s 翻转,枚举分界点,前一部分用 manacher 预处理,后一部分用 z-algorithm 预处理即可。
#include
using namespace std;
template<typename T> vector<int> z_algorithm(const T &a) {
int n = a.size();
vector<int> z(n);
z[0] = n;
int l = -1, r = -1;
for (int i = 1; i < n; ++i) {
z[i] = i >= r ? 0 : min(r - i, z[i - l]);
while (i + z[i] < n && a[i + z[i]] == a[z[i]]) {
++z[i];
}
if (i + z[i] > r) {
l = i;
r = i + z[i];
}
}
return z;
}
template<typename T> vector<int> manacher(const T &a) {
int n = a.size(), m = (n << 1) - 1;
T b(m, -1);
vector<int> p(m, 0);
for (int i = 0; i < n; ++i) {
b[i << 1] = a[i];
}
int x = 0;
for (int i = 1; i < m; ++i) {
if (i <= x + p[x]) {
p[i] = min(p[(x << 1) - i], x + p[x] - i);
}
while (i - p[i] - 1 >= 0 && i + p[i] + 1 < m && b[i - p[i] - 1] == b[i + p[i] + 1]) {
++p[i];
}
if (i + p[i] >= x + p[x]) {
x = i;
}
}
for (int i = 0; i < m; ++i) {
if (i - p[i] == 0 || i + p[i] == m - 1) {
++p[i];
}
}
for (int i = 0; i < m; ++i) {
p[i] >>= 1;
}
return p;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
string s, t;
cin >> s >> t;
reverse(s.begin(), s.end());
vector<int> z = z_algorithm(t + '#' + s);
vector<int> p = manacher(s);
vector<int> tag(s.length() + 1);
for (int i = 0; i < p.size(); ++i) {
++tag[i + 1 >> 1];
--tag[(i >> 1) + p[i] + 1];
}
for (int i = 0; i < s.length(); ++i) {
tag[i + 1] += tag[i];
}
long long answer = 0;
for (int i = 0; i < s.length() - 1; ++i) {
answer += (long long)z[i + t.length() + 2] * tag[i];
}
printf("%lld\n", answer);
return 0;
}