练习选讲(2023.9)
9 月
9.1
P5546 [POI2000] 公共串:二分,哈希,SA(紫)
二分长度 unordered_map
存储对于每一个字符串,当前长度的哈希值是否出现过。最后再枚举第一个字符串中出现的长度为
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <unordered_map>
using namespace std;
typedef unsigned long long ull;
const ull P = 19260817;
int n, minlen = 2000; ull p[2010], h[10][2010];
char s[10][2010];
bool check(int len) {
unordered_map<ull, bool> nums[10];
for (int i = 2; i <= n; ++i) {
for (int j = len; j <= strlen(s[i]+1); ++j) {
ull num = h[i][j] - h[i][j-len] * p[len];
nums[i][num] = 1;
}
}
for (int i = len; i <= strlen(s[1]+1); ++i) {
ull num = h[1][i] - h[1][i-len] * p[len];
bool check = 1;
for (int j = 2; j <= n; ++j) check &= nums[j][num];
if (check) return 1;
}
return 0;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> s[i]+1;
if (n == 1) return printf("%d\n", strlen(s[1]+1)), 0;
p[0] = 1;
for (int i = 1; i <= 2000; ++i) p[i] = p[i-1] * P;
for (int i = 1; i <= n; ++i) {
minlen = min(minlen, (int)strlen(s[i]+1));
for (int j = 1; j <= strlen(s[i]+1); ++j)
h[i][j] = h[i][j-1] * P + (s[i][j]-'a');
}
int l = -1, r = minlen+1;
while (l+1 != r) {
int mid = l + r >> 1;
if (check(mid)) l = mid;
else r = mid;
}
printf("%d\n", l);
return 0;
}
P8818 [CSP-S 2022] 策略游戏:st 表(绿)
显然我们应该考虑后手的情况:
1 后手无负数
1.1 先手有正数:此时先手会取区间内最大值,后手只能取区间内最小值。
1.2 先手无正数:此时先手只能取区间内最大值,后手会取区间内最大值。
2 后手无正数
2.1 先手有负数:此时先手会取区间内最小值(负负得正,相乘后变为最大值),后手会取区间内最大值。
2.2 先手无负数:此时先手只能取区间内最小值,后手会取区间内最小值。
3 后手既有正数又有负数
3.1 先手有
3.2 先手无正数:此时先手只能取区间内最大值,后手会取区间内最大值。
3.3 先手无负数:此时先手只能取区间内最小值,后手会取区间内最小值。
3.4 先手既有正数又有负数:先手可以在 3.2 和 3.3 两种情况中取最大值,即
观察上面这
- 区间内最大/小值;
- 区间内最大非正数,区间内最小非负数。
用
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e5+10, INF = 2e9;
int n, m, q;
int st[3][5][N][20];
// 1: 区间内最大值, 2: 区间内最小值, 3: 区间内最大非正数, 4: 区间内最小非负数
int query(int p, int op, int l, int r) {
int k = log2(r-l+1);
if (op & 1) return max(st[p][op][l][k], st[p][op][r-(1<<k)+1][k]);
else return min(st[p][op][l][k], st[p][op][r-(1<<k)+1][k]);
}
int main() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
st[1][1][i][0] = x, st[1][2][i][0] = x;
st[1][3][i][0] = (x > 0) ? -INF : x, st[1][4][i][0] = (x < 0) ? INF : x;
}
for (int i = 1; i <= m; ++i) {
int x; scanf("%d", &x);
st[2][1][i][0] = x, st[2][2][i][0] = x;
st[2][3][i][0] = (x > 0) ? -INF : x, st[2][4][i][0] = (x < 0) ? INF : x;
}
for (int i = 1; (1<<i) <= n; ++i) {
for (int j = 1; j+(1<<i)-1 <= n; ++j)
st[1][1][j][i] = max(st[1][1][j][i-1], st[1][1][j+(1<<i-1)][i-1]),
st[1][2][j][i] = min(st[1][2][j][i-1], st[1][2][j+(1<<i-1)][i-1]),
st[1][3][j][i] = max(st[1][3][j][i-1], st[1][3][j+(1<<i-1)][i-1]),
st[1][4][j][i] = min(st[1][4][j][i-1], st[1][4][j+(1<<i-1)][i-1]);
}
for (int i = 1; (1<<i) <= m; ++i) {
for (int j = 1; j+(1<<i)-1 <= m; ++j)
st[2][1][j][i] = max(st[2][1][j][i-1], st[2][1][j+(1<<i-1)][i-1]),
st[2][2][j][i] = min(st[2][2][j][i-1], st[2][2][j+(1<<i-1)][i-1]),
st[2][3][j][i] = max(st[2][3][j][i-1], st[2][3][j+(1<<i-1)][i-1]),
st[2][4][j][i] = min(st[2][4][j][i-1], st[2][4][j+(1<<i-1)][i-1]);
}
int l1, r1, l2, r2;
while (q -- ) {
scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
int maxa = query(1, 1, l1, r1), mina = query(1, 2, l1, r1), maxb = query(2, 1, l2, r2), minb = query(2, 2, l2, r2);
if (minb >= 0) {
if (maxa > 0) printf("%lld\n", (ll)maxa*minb);
else printf("%lld\n", (ll)maxa*maxb);
} else if (maxb <= 0) {
if (mina < 0) printf("%lld\n", (ll)mina*maxb);
else printf("%lld\n", (ll)mina*minb);
} else {
int maxxa = query(1, 3, l1, r1), minna = query(1, 4, l1, r1);
if (maxxa == 0) puts("0");
else if (maxa < 0) printf("%lld\n", (ll)maxa*maxb);
else if (mina > 0) printf("%lld\n", (ll)mina*minb);
else printf("%lld\n", max((ll)maxxa*maxb, (ll)minna*minb));
}
}
return 0;
}
补之前的 ABC 317:
ABC317A. Potions:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, h, x, a[110];
int main() {
scanf("%d%d%d", &n, &h, &x);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
if (h+a[i] >= x) printf("%d\n", i), exit(0);
}
}
ABC317B. MissingNo:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, minl = 1001, nums[1100];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int x; scanf("%d", &x);
minl = min(minl, x), nums[x] ++;
}
for (int i = minl; i <= minl+n; ++i) {
if (!nums[i])
printf("%d\n", i), exit(0);
}
}
ABC317C. Remembering the Days:枚举,dfs(橙)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n, m, ans, a[15], g[15][15];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
g[u][v] = g[v][u] = w;
}
for (int i = 1; i <= n; ++i) a[i] = i;
while (true) {
int tot = 0;
for (int i = 1; i < n; ++i) {
if (g[a[i]][a[i+1]]) tot += g[a[i]][a[i+1]];
else break;
}
ans = max(ans, tot);
if (!next_permutation(a+1, a+n+1)) break;
}
printf("%d\n", ans);
return 0;
}
ABC317D. President:dp(黄)
令
同样的,当
否则,
答案为
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const int N = 110, M = 1e5+10;
int n, ans, tot, x[N], y[N], z[N], f[N][M];
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) scanf("%lld%lld%lld", &x[i], &y[i], &z[i]), tot += z[i];
for (int i = 1; i <= tot; ++i) f[1][i] = 1e18;
f[1][z[1]] = (x[1] > y[1]) ? 0 : y[1]-(x[1]+y[1])/2;
for (int i = 2; i <= n; ++i) {
for (int j = 0; j <= tot; ++j) {
f[i][j] = (int)1e18;
if (x[i] > y[i] && j >= z[i]) f[i][j] = min(f[i][j], f[i-1][j-z[i]]);
if (x[i] < y[i]) f[i][j] = min(f[i-1][j], (j>=z[i])?(y[i]-(x[i]+y[i])/2+f[i-1][j-z[i]]):(int)1e18);
}
}
ans = 1e18;
for (int i = (tot+1)/2; i <= tot; ++i) ans = min(ans, f[n][i]);
printf("%lld\n", ans);
return 0;
}
ABC317E. Avoid Eye Contact:bfs(黄)
实际上直接暴力处理出地图就好了,但我赛时脑抽了写了个奇丑的二分……
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
typedef pair<int, int> pii;
int n, m, dist[2010][2010]; pii S, T;
vector<pii> row[2010], col[2010];
char c[2010][2010];
bool check(int x, int y) {
if (!(x && y && (x <= n) && (y <= m) && (c[x][y] == '.' || c[x][y] == 'G'))) return 0;
int l = upper_bound(row[x].begin(), row[x].end(), make_pair(y, 0)) - row[x].begin() - 1;
int r = upper_bound(row[x].begin(), row[x].end(), make_pair(y, 0)) - row[x].begin();
int u = upper_bound(col[y].begin(), col[y].end(), make_pair(x, 0)) - col[y].begin() - 1;
int d = upper_bound(col[y].begin(), col[y].end(), make_pair(x, 0)) - col[y].begin();
return ((l == -1 || row[x][l].second > -1) && (r == row[x].size() || row[x][r].second < 1) &&
(u == -1 || col[y][u].second < 1) && (d == col[y].size() || col[y][d].second > -1));
}
void bfs() {
queue<pii> q; q.push(S), dist[S.first][S.second] = 0;
while (q.size()) {
auto t = q.front(); q.pop();
int x = t.first, y = t.second;
for (int i = 0; i < 4; ++i) {
int x_ = x+dx[i], y_ = y+dy[i];
if (dist[x_][y_] > dist[x][y]+1 && check(x_, y_)) dist[x_][y_] = dist[x][y]+1, q.push({x_, y_});
}
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> c[i]+1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
dist[i][j] = 1e9;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
if (c[i][j] == '.') continue;
else if (c[i][j] == '#') row[i].push_back({j, 0}), col[j].push_back({i, 0});
else if (c[i][j] == 'S') S = {i, j};
else if (c[i][j] == 'G') T = {i, j};
else if (c[i][j] == '>') row[i].push_back({j, -1}), col[j].push_back({i, 0});
else if (c[i][j] == '<') row[i].push_back({j, 1}), col[j].push_back({i, 0});
else if (c[i][j] == '^') col[j].push_back({i, -1}), row[i].push_back({j, 0});
else if (c[i][j] == 'v') col[j].push_back({i, 1}), row[i].push_back({j, 0});
}
}
bfs();
if (dist[T.first][T.second] == 1e9) puts("-1");
else printf("%d\n", dist[T.first][T.second]);
return 0;
}
ABC317F. Nim:数位 dp(蓝)
注意到
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int P = 998244353;
long long n;
int x, y, z, r[65], f[65][15][15][15][2][2][2][2][2][2];
int dfs(int u, int a, int b, int c, bool l1, bool l2, bool l3, bool z1, bool z2, bool z3) {
if (u < 0) return ((!a) && (!b) && (!c) && (!z1) && (!z2) && (!z3));
if (f[u][a][b][c][l1][l2][l3][z1][z2][z3] != -1) return f[u][a][b][c][l1][l2][l3][z1][z2][z3];
int res = 0;
for (int i = 0; i <= (l1 ? r[u] : 1); ++i) {
for (int j = 0; j <= (l2 ? r[u] : 1); ++j) {
for (int k = 0; k <= (l3 ? r[u] : 1); ++k) {
if (i ^ j ^ k) continue;
res += dfs(u-1, (a+(1ll<<u)*i)%x, (b+(1ll<<u)*j)%y, (c+(1ll<<u)*k)%z, (l1&&(i==r[u])), (l2&&(j==r[u])), (l3&&(k==r[u])), (z1&&(!i)), (z2&&(!j)), (z3&&(!k)));
res %= P;
}
}
}
return f[u][a][b][c][l1][l2][l3][z1][z2][z3] = res;
}
int main() {
memset(f, -1, sizeof f);
scanf("%lld%d%d%d", &n, &x, &y, &z);
for (int i = 62; i >= 0; --i) r[i] = ((n >> i) & 1);
printf("%d\n", dfs(62, 0, 0, 0, 1, 1, 1, 1, 1, 1));
return 0;
}
9.2
ABC317G. Rearranging:二分图,最大流(紫)
将这个问题转化为一个图论问题,若
Hall 定理:二分图有完美匹配的充要条件是,对于左部点集的任意一个子集
,它的邻居集合(可重集) 均满足 。 实际上,Hall 定理可以推广到有
个完美匹配的形式,此时需要满足 。
我们先给出做法:不断寻找完美匹配,找到后把该完美匹配内的所有边删除,重复
通过 Dinic 算法求解二分图的完美匹配,时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 410, M = 50010, INF = 2e9;
int n, m, S, T, a[N][N], ans[N][N];
int idx = 1, cnt, e[M], ne[M], h[N], c[M];
int d[N], cur[N];
void add(int u, int v, int w) {
e[++ idx] = v, ne[idx] = h[u], c[idx] = w, h[u] = idx;
}
bool bfs() {
memset(d, 0, sizeof d);
queue<int> q; q.push(S), d[S] = 1, cur[S] = h[S];
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = h[u]; i != -1; i = ne[i]) {
int v = e[i];
if (!d[v] && c[i] > 0) {
d[v] = d[u]+1, q.push(v), cur[v] = h[v];
if (v == T) return 1;
}
}
}
return 0;
}
int dinic(int u, int mf) {
if (u == T) return mf;
int sum = 0;
for (int i = cur[u]; i != -1; i = ne[i]) {
cur[u] = i;
int v = e[i];
if (d[v] == d[u]+1 && c[i] > 0) {
int f = dinic(v, min(mf, c[i]));
c[i] -= f, c[i^1] += f, sum += f, mf -= f;
if (!mf) break;
}
}
if (!sum) d[u] = 0;
return sum;
}
int MaxFlow() {
int flow = 0;
while (bfs()) flow += dinic(S, INF);
return flow;
}
int main() {
S = 0, T = 400;
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
scanf("%d", &a[i][j]);
add(i, a[i][j]+n, 1), add(a[i][j]+n, i, 0);
}
}
cnt = idx;
for (int i = 1; i <= n; ++i) add(S, i, 1), add(i, S, 0), add(i+n, T, 1), add(T, i+n, 0);
for (int j = 1; j <= m; ++j) {
if (MaxFlow() != n) return puts("No"), 0;
for (int i = 3; i <= cnt; i += 2) {
if (!c[i]) continue;
int u = e[i], v = e[i^1];
ans[u][j] = v-n;
c[i] = c[i^1] = 0;
}
for (int i = cnt+2; i <= idx; i += 2) {
if (!c[i]) continue;
c[i^1] = 1, c[i] = 0;
}
}
puts("Yes");
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
printf("%d ", ans[i][j]);
puts("");
}
return 0;
}
CF540E. Infinite Inversions:树状数组(紫)
(双倍经验:P2448 无尽的生命)
答案可以分为两种情况:
- 两个调换过位置的数构成的逆序对:
这种情况直接用树状数组维护即可。
- 一个调换过位置的数和一个没有调换过位置的数构成的逆序对:
令
显然,两个没有调换过位置的数不可能构成逆序对。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
const int N = 2e5+10;
int n, a[N], c[N]; long long ans; pii oper[N];
vector<int> pos;
int find(int x) {
return lower_bound(pos.begin(), pos.end(), x) - pos.begin() + 1;
}
int lowbit(int x) {
return x & -x;
}
void add(int x, int k) {
for (int i = x; i < N; i += lowbit(i)) c[i] += k;
}
int query(int x) {
int sum = 0;
for (int i = x; i > 0; i -= lowbit(i)) sum += c[i];
return sum;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int x, y; scanf("%d%d", &x, &y);
oper[i] = {x, y}; pos.push_back(x), pos.push_back(y);
}
sort(pos.begin(), pos.end());
pos.erase(unique(pos.begin(), pos.end()), pos.end());
for (int i = 1; i <= pos.size(); ++i) a[i] = pos[i-1];
for (int i = 1; i <= n; ++i) swap(a[find(oper[i].first)], a[find(oper[i].second)]);
for (int i = 1; i <= pos.size(); ++i) {
int x = query(find(a[i]));
ans += (find(a[i])-1 - x) + abs(a[i]-pos[i-1]-(find(a[i])-find(pos[i-1])));
add(find(a[i]), 1);
}
printf("%lld\n", ans);
return 0;
}
P5854 【模板】笛卡尔树:单调栈,笛卡尔树(蓝)
笛卡尔树是一种特殊的二叉搜索树,其一个节点上有两个信息
在保证
每次插入一个新节点时,为了确保二叉搜索树的性质得到满足,我们需要将新节点插入到尽可能靠右的位置。也就是说,我们需要维护从根节点一直到右儿子形成的链。设当前要插入的点为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e7+10;
int n, top, p[N], L[N], R[N], stk[N]; ll ans1, ans2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<23],*p1=buf,*p2=buf;
template <typename T>
void read(T &x) {
x = 0;
register int flag = 1;
static char c = getchar();
while (!isdigit(c)) {
if (c == '-') flag = -1;
c = getchar();
}
while (isdigit(c)) {
x = x * 10 + c - '0';
c = getchar();
}
x *= flag;
}
void build() {
for (int i = 1; i <= n; ++i) {
while (top && p[stk[top]] > p[i]) top --;
if (!top) L[i] = stk[top+1];
else L[i] = R[stk[top]], R[stk[top]] = i;
stk[++ top] = i;
}
}
int main() {
read(n);
for (int i = 1; i <= n; ++i) read(p[i]);
build();
for (int i = 1; i <= n; ++i) ans1 ^= 1ll * i * (L[i]+1), ans2 ^= 1ll * i * (R[i]+1);
printf("%lld %lld\n", ans1, ans2);
return 0;
}
9.3
P3538 [POI2012] OKR-A Horrible Poem:字符串哈希,线性筛(蓝)
本题需要用到循环节的几个关键性质:
-
若
,则 为 的循环节长度。 -
循环节
循环次数 总长度,即总长度的质因子可分为循环节和循环次数。 -
最小循环节的倍数也是循环节。
知道这些之后,我们可以将区间长度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
const int N = 5e5+10;
const ull P = 998244353;
int n, m; char s[N];
ull h[N], power[N];
int cnt, prime[N], p[N]; bool st[N];
// p[i]表示i的最小质因子
void Euler() {
st[0] = st[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[++ cnt] = i, p[i] = i;
for (int j = 1; j <= cnt && prime[j]*i <= n; ++j) {
st[i*prime[j]] = 1, p[i*prime[j]] = prime[j];
if (i % prime[j] == 0) break;
}
}
}
ull get_hash(int l, int r) {
return h[r] - h[l-1] * power[r-l+1];
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> s+1 >> m;
Euler();
power[0] = 1; for (int i = 1; i <= n; ++i) power[i] = power[i-1] * P;
for (int i = 1; i <= n; ++i) h[i] = h[i-1] * P + (s[i]-'a');
int l, r, ans, len;
while (m -- ) {
cin >> l >> r;
ans = len = r-l+1;
while (len > 1) {
int newlen = ans / p[len];
if (get_hash(l, r-newlen) == get_hash(l+newlen, r)) ans /= p[len];
len /= p[len];
}
cout << ans << '\n';
}
return 0;
}
P1377 [TJOI2011] 树的序:笛卡尔树(蓝)
这题与板子题的区别在于,权值符合二叉搜索树的性质,编号符合小根堆的性质(因为始终先插入父亲,再插入儿子)。那么只需要在读入时调换一下位置就好。
如何求出字典序最小的插入顺序?由于权值是二叉搜索树,所以先序遍历输出答案即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, p[N], L[N], R[N];
int top, stk[N];
void build() {
for (int i = 1; i <= n; ++i) {
while (top && p[stk[top]] > p[i]) top --;
if (!top) L[i] = stk[top+1];
else L[i] = R[stk[top]], R[stk[top]] = i;
stk[++ top] = i;
}
}
void dfs(int u) {
if (!u) return ;
printf("%d ", u), dfs(L[u]), dfs(R[u]);
}
int main() {
scanf("%d", &n);
for (int i = 1, x; i <= n; ++i) scanf("%d", &x), p[x] = i;
build(), dfs(stk[1]);
return 0;
}
P8773 [蓝桥杯 2022 省 A] 选数异或:st 表(绿)
对于每一个
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 1e5+10, M = 1 << 21;
int n, m, x, a[N], L[N], last[M], st[N][20];
int query(int l, int r) {
int k = log2(r-l+1);
return max(st[l][k], st[r-(1<<k)+1][k]);
}
int main() {
scanf("%d%d%d", &n, &m, &x);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) last[a[i]] = i, st[i][0] = L[i] = last[x^a[i]];
for (int j = 1; (1<<j) <= n; ++j) {
for (int i = 1; i+(1<<j)-1 <= n; ++i)
st[i][j] = max(st[i][j-1], st[i+(1<<j-1)][j-1]);
}
int l, r;
while (m -- ) {
scanf("%d%d", &l, &r);
int pos = query(l, r);
if (l <= pos && pos <= r) puts("yes");
else puts("no");
}
return 0;
}
CF618F. Double Knapsack:构造(紫)
可以猜测,一定有解且为两个连续子段。
令
对于每一个
假设
由于
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
bool check = 1;
int n, a[N], b[N], c[N], num[N]; ll sa[N], sb[N];
int main() {
scanf("%d", &n); memset(num, -1, sizeof num);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), sa[i] = (ll)sa[i-1]+a[i];
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]), sb[i] = (ll)sb[i-1]+b[i];
if (sa[n] > sb[n]) for (int i = 1; i <= n; ++i) swap(sa[i], sb[i]), check = 0;
int now = 0;
for (int i = 0; i <= n; ++i) {
while (now < n && sa[i] >= sb[now+1]) now ++;
c[i] = now;
if (num[sa[i]-sb[c[i]]] == -1) num[sa[i]-sb[c[i]]] = i;
else {
int last = num[sa[i]-sb[c[i]]];
if (check) {
printf("%d\n", i-last);
for (int j = last+1; j <= i; ++j) printf("%d ", j); puts("");
printf("%d\n", c[i]-c[last]);
for (int j = c[last]+1; j <= c[i]; ++j) printf("%d ", j); puts("");
} else {
printf("%d\n", c[i]-c[last]);
for (int j = c[last]+1; j <= c[i]; ++j) printf("%d ", j); puts("");
printf("%d\n", i-last);
for (int j = last+1; j <= i; ++j) printf("%d ", j); puts("");
}
return 0;
}
}
}
P4398 [JSOI2008] Blue Mary的战役地图:哈希(蓝)
哈希乱搞一下就过了。时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef unsigned long long ull;
const int N = 55;
const ull P1 = 19260817, P2 = 19491001; // 行和列选取不同的进制
int n, a[2][N][N]; ull h[2][N][N], power[N];
ull get_hash(int x, int y, int k, int op) {
ull res = 0;
for (int i = x; i <= x+k-1; ++i) res = res*P2 + h[op][i][y+k-1]-h[op][i][y-1]*power[k];
}
int main() {
scanf("%d", &n);
for (int k = 0; k <= 1; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
scanf("%d", &a[k][i][j]);
}
}
power[0] = 1;
for (int i = 1; i <= n; ++i) power[i] = power[i-1]*P1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
h[0][i][j] = h[0][i][j-1]*P1+a[0][i][j], h[1][i][j] = h[1][i][j-1]*P2+a[1][i][j];
}
for (int k = n; k >= 1; --k) {
unordered_map<ull, int> nums;
for (int i = 1; i <= n-k+1; ++i) {
for (int j = 1; j <= n-k+1; ++j)
nums[get_hash(i, j, k, 0)] = 1;
}
for (int i = 1; i <= n-k+1; ++i) {
for (int j = 1; j <= n-k+1; ++j)
if (nums[get_hash(i, j, k, 1)])
return printf("%d\n", k), 0;
}
}
return puts("0"), 0;
}
CF1092F. Tree with Maximum Cost:换根 dp(绿)
板子题。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n, a[N], dist[N]; ll sum, ans, siz[N], f[N];
vector<int> e[N];
void dfs(int u, int fa) {
siz[u] = a[u], f[1] += (ll)dist[u]*a[u];
for (auto v : e[u]) {
if (v == fa) continue;
dist[v] = dist[u]+1, dfs(v, u), siz[u] += siz[v];
}
}
void dp(int u, int fa) {
ans = max(ans, f[u]);
for (auto v : e[u]) {
if (v == fa) continue;
f[v] = f[u] - siz[v] + (sum-siz[v]), dp(v, u);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), sum += a[i];
for (int i = 1; i < n; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
dfs(1, 1), dp(1, 1);
printf("%lld\n", ans);
return 0;
}
9.4
ABC259D. Circumferences:并查集(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 3010;
int n, sx, sy, tx, ty, p[N];
struct Circ {
int x, y, r;
} c[N];
int find(int x) {return p[x] = (p[x] == x) ? p[x] : find(p[x]);}
ll get_dist(int x1, int y1, int x2, int y2) {
return (ll)(x1-x2)*(x1-x2)+(ll)(y1-y2)*(y1-y2);
}
int main() {
scanf("%d%d%d%d%d", &n, &sx, &sy, &tx, &ty);
p[0] = 0, p[n+1] = n+1;
for (int i = 1; i <= n; ++i) scanf("%d%d%d", &c[i].x, &c[i].y, &c[i].r), p[i] = i;
for (int i = 1; i <= n; ++i) {
if (get_dist(sx, sy, c[i].x, c[i].y) == (ll)c[i].r*c[i].r) p[0] = i;
if (get_dist(tx, ty, c[i].x, c[i].y) == (ll)c[i].r*c[i].r) p[n+1] = i;
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (find(i) == find(j)) continue;
if (get_dist(c[i].x, c[i].y, c[j].x, c[j].y) <= (ll)(c[i].r+c[j].r)*(c[i].r+c[j].r) &&
get_dist(c[i].x, c[i].y, c[j].x, c[j].y) >= (ll)abs(c[i].r-c[j].r)*abs(c[i].r-c[j].r))
p[find(i)] = find(j);
}
}
(find(0) == find(n+1)) ? puts("Yes") : puts("No");
return 0;
}
ABC259E. LCM on Whiteboard:数学(绿)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <unordered_map>
using namespace std;
typedef pair<int, int> pii;
const int N = 2e5+10;
int n, ans, idx; bool Check; vector<pii> e[N];
unordered_map<int, int> nums; pii maxl[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int m; scanf("%d", &m), e[i].push_back({m, 0});
for (int j = 1; j <= m; ++j) {
int p, r; scanf("%d%d", &p, &r);
if (!nums[p]) nums[p] = ++ idx; p = nums[p];
e[i].push_back({p, r});
if (maxl[p].first < r) maxl[p] = {r, 1};
else if (maxl[p].first == r) maxl[p].second ++;
}
}
for (int i = 1; i <= n; ++i) {
int m = e[i][0].first;
bool check = 0;
for (int j = 1; j <= m; ++j) {
int p = e[i][j].first, r = e[i][j].second;
if (maxl[p].first == r && maxl[p].second == 1) {check = 1; break;}
}
if (check) ans ++;
else if (!Check) ans ++, Check = 1;
}
printf("%d\n", ans);
return 0;
}
ABC259F. Select Edges:树形 dp(蓝)
令 vector
里排序就好了。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5+10;
int n, d[N]; ll f[N][2];
vector<pii> e[N];
void dfs(int u, int fa) {
for (auto edge : e[u]) {
int v = edge.first, w = edge.second;
if (v == fa) {
if (d[u]) f[u][1] += w;
continue ;
}
dfs(v, u);
}
vector<ll> nums;
for (auto edge : e[u]) {
int v = edge.first, w = edge.second;
if (v == fa) continue;
f[u][0] += f[v][0], f[u][1] += f[v][0];
nums.push_back((d[v]?f[v][1]:0)-f[v][0]);
}
sort(nums.begin(), nums.end(), greater<ll>());
int tot = 0;
for (auto num : nums) {
if (num <= 0) break;
tot ++;
if (d[u]-tot >= 0) f[u][0] += num;
if (d[u]-tot >= 1) f[u][1] += num;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &d[i]);
for (int i = 1; i < n; ++i) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
e[u].push_back({v, w}), e[v].push_back({u, w});
}
dfs(1, 0);
printf("%lld\n", max(f[1][0], f[1][1]));
return 0;
}
9.5
ABC258D. Trophy:枚举(橙)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <climits>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5+10;
int n, x, ans = LLONG_MAX, a[N], b[N];
signed main() {
scanf("%lld%lld", &n, &x);
for (int i = 1; i <= n; ++i) scanf("%lld%lld", &a[i], &b[i]);
int tot = 0, minb = 2e9;
for (int i = 1; i <= min(n, x); ++i) {
tot += a[i]+b[i], minb = min(minb, b[i]);
ans = min(ans, tot+minb*(x-i));
}
printf("%lld\n", ans);
return 0;
}
ABC258E. Packing Potatoes:枚举,二分,前缀和(绿)
注意到我们确定一个段的起点时,终点也随之确定,所以最多有
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define int long long
const int N = 2e5+10;
int n, q, x, w[N], c[N], s[N];
unordered_map<int, int> circ;
int find(int pos, int tot) {
int l = pos, r = n;
while (l < r) {
int mid = l + r >> 1;
if (s[mid]-s[pos-1] >= tot) r = mid;
else l = mid+1;
}
return l;
}
int calc(int &pos) {
int now = pos, sum = s[n]-s[now-1], num = 0;
if (sum >= x) return pos = find(now, x), pos-now+1;
num += (x-sum) / s[n];
sum = x - sum - num * s[n];
return pos = find(1, sum), num*n+pos+n-now+1;
}
signed main() {
scanf("%lld%lld%lld", &n, &q, &x);
for (int i = 1; i <= n; ++i) scanf("%lld", &w[i]), s[i] = s[i-1]+w[i];
int pos = 1, idx = 1;
c[1] = calc(pos), circ[1] = 1;
while (true) {
pos = pos % n + 1;
if (circ[pos]) break;
idx ++, circ[pos] = idx;
c[idx] = calc(pos);
}
while (q -- ) {
int k; scanf("%lld", &k);
if (k <= idx) printf("%lld\n", c[k]);
else k -= circ[pos]-1, printf("%lld\n", (k%(idx-circ[pos]+1))?c[k%(idx-circ[pos]+1)+circ[pos]-1]:c[idx]);
}
return 0;
}
ABC258G. Triangle:枚举(绿)
枚举
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <bitset>
using namespace std;
typedef long long ll;
const int N = 3010;
int n; ll ans;
bitset<N> a[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
int x; scanf("%1d", &x);
if (j <= i) a[i][j] = 0;
else a[i][j] = x;
}
}
for (int i = 1; i <= n; ++i) {
for (int j = i+1; j <= n; ++j)
if (a[i][j]) ans += (a[i]&a[j]).count();
}
printf("%lld\n", ans);
return 0;
}
9.6
ABC257D. Jumping Takahashi 2:二分,bfs(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#define int long long
const int N = 210;
struct Dot {
int x, y, p;
} dot[N];
int n; bool st[N];
bool bfs(int u, int x) {
int cnt = 1; queue<int> q; memset(st, 0, sizeof st);
q.push(u), st[u] = 1;
while (q.size()) {
int u = q.front(); q.pop();
for (int i = 1; i <= n; ++i) {
if (st[i]) continue;
if (dot[u].p * x >= abs(dot[u].x-dot[i].x)+abs(dot[u].y-dot[i].y)) q.push(i), st[i] = 1, cnt ++;
}
}
return (cnt == n);
}
bool check(int x) {
for (int i = 1; i <= n; ++i) if (bfs(i, x)) return 1;
return 0;
}
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) scanf("%lld%lld%lld", &dot[i].x, &dot[i].y, &dot[i].p);
int l = 1, r = 8e9;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid+1;
}
printf("%lld\n", l);
return 0;
}
ABC257E. Addition and Multiplication 2:贪心(绿)
可以想到先选价值最小的让位数最大,再从最高位开始调整。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, num[15];
int main() {
scanf("%d", &n);
int minl = 1e9;
for (int i = 1; i <= 9; ++i) scanf("%d", &num[i]), minl = min(minl, num[i]);
int len = n/minl; n -= minl*len;
for (int i = 1; i <= len; ++i) {
int j;
for (j = 9; num[j] != minl; --j) if (num[j]-minl <= n) break;
n -= num[j]-minl, printf("%d", j);
}
return 0;
}
ABC257F. Teleporter Setting:最短路(绿)
直接按照题意建边,将
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;
typedef pair<int, int> pii;
const int N = 3e5+10, inf = 2e8;
int n, m, dist[2][N]; bool st[N];
vector<int> dots, e[N];
void dijkstra(int op, int s) {
for (int i = 0; i <= n; ++i) dist[op][i] = inf, st[i] = 0;
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, s}), dist[op][s] = 0;
while (q.size()) {
auto t = q.top(); q.pop();
int u = t.second, dis = t.first;
if (st[u]) continue;
st[u] = 1;
for (auto v : e[u]) if (dist[op][v] >= dis+1) dist[op][v] = dis+1, q.push({dist[op][v], v});
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
dijkstra(0, 1), dijkstra(1, n);
for (int i = 1; i <= n; ++i) {
int ans = min(dist[0][n], min(dist[0][i]+dist[1][0], dist[1][i]+dist[0][0]));
printf("%d ", (ans>=inf)?-1:ans);
}
return 0;
}
ABC257G. Prefix Concatenation:KMP,贪心(蓝)
令
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 5e5+10;
int n, m, ans, ne[N], f[N];
char S[N], T[N];
void get_next() {
ne[1] = 0;
for (int i = 2, j = 0; i <= n; ++i) {
while (S[j+1] != S[i] && j) j = ne[j];
if (S[j+1] == S[i]) ne[i] = ++ j;
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> S+1 >> T+1;
n = strlen(S+1), m = strlen(T+1);
get_next();
for (int i = 1, j = 0; i <= m; ++i) {
while (S[j+1] != T[i] && j) j = ne[j];
if (S[j+1] == T[i]) ++ j;
f[i] = j; if (!f[i]) return puts("-1"), 0;
if (j == n) j = ne[j];
}
for (int i = m; i >= 1; i -= f[i]) ans ++;
printf("%d\n", ans);
return 0;
}
9.7
ABC256D. Union of Interval:排序(橙)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
int n; vector<pii> range;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int l, r; scanf("%d%d", &l, &r);
range.push_back({l, r});
}
sort(range.begin(), range.end());
int L = range[0].first, R = range[0].second;
for (int i = 1; i < n; ++i) {
if (range[i].first <= R) R = max(R, range[i].second);
else printf("%d %d\n", L, R), L = range[i].first, R = range[i].second;
}
printf("%d %d\n", L, R);
return 0;
}
ABC256E. Takahashi's Anguish:dfs(黄)
考虑将这个问题转化为一个图论问题,我们从
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 2e5+10;
int n, x[N], c[N]; ll ans; bool st[N], inc[N], check[N];
vector<pii> e[N];
void dfs(int u) {
st[u] = 1, check[u] = 1;
for (auto edge : e[u]) {
int v = edge.first;
if (st[v]) {inc[v] = 1; continue;}
if (check[v]) continue;
dfs(v);
}
st[u] = 0;
}
void calc(int u, int &minl) {
st[u] = 1;
for (auto edge : e[u]) {
int v = edge.first, w = edge.second;
minl = min(minl, w);
if (st[v]) continue;
calc(v, minl);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &x[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &c[i]);
for (int i = 1; i <= n; ++i) e[i].push_back({x[i], c[i]});
for (int i = 1; i <= n; ++i) if (!check[i]) dfs(i);
for (int i = 1; i <= n; ++i) {
if (!inc[i] || st[i]) continue;
int now = 1e9; calc(i, now);
ans += now;
}
printf("%lld\n", ans);
return 0;
}
ABC256F. Cumulative Cumulative Cumulative Sum:数学,树状数组,线段树(蓝)
纯粹的推式子。
树状数组维护这三个东西即可。
注意
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5+10, P = 998244353, inv = 499122177;
int n, m, a[N], c[5][N];
int lowbit(int x) {
return x & -x;
}
void add(int op, int x, int k) {
for (int i = x; i <= n; i += lowbit(i)) c[op][i] = (c[op][i] + k) % P;
}
int query(int op, int x) {
int sum = 0;
for (int i = x; i >= 1; i -= lowbit(i)) sum = (sum + c[op][i]) % P;
return sum;
}
void modify(int x, int v) {
int t = ((-a[x]+v) % P + P) % P;
add(1, x, t%P), add(2, x, t*x%P), add(3, x, t*x%P*x%P), a[x] = v;
}
int get_sum(int x) {
int sum = (((__int128)(x*x+3*x+2)*query(1, x)%P - (2*x+3)*query(2, x)%P + query(3, x)) * inv % P + P) % P;
return sum;
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]), add(1, i, a[i]%P), add(2, i, a[i]*i%P), add(3, i, a[i]*i%P*i%P);
int op, x, v;
while (m -- ) {
scanf("%lld%lld", &op, &x);
if (op == 1) scanf("%lld", &v), modify(x, v);
else printf("%lld\n", get_sum(x));
}
return 0;
}
ABC256Ex. I like Query Problem:珂朵莉树,分块,势能线段树(紫)
看到区间推平立马想到珂朵莉树,但是区间整除和区间求和会爆炸,考虑用分块优化珂朵莉树,即对每一个块建一个珂朵莉树并记录快内元素和,在修改时也统计块内元素和。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
typedef long long ll;
const int N = 5e5+10;
int n, m, p, a[N], block[N], L[N], R[N];
struct Node {
int l, r;
mutable int v;
Node (int l, int r = 0, int v = 0) : l(l), r(r), v(v) {};
bool operator < (const Node &T) const {
return l < T.l;
}
};
typedef set<Node>::iterator iter;
struct Block {
int l, r; ll sum; set<Node> s;
iter split(int pos) {
iter it = s.lower_bound(Node(pos));
if (it != s.end() && it->l == pos) return it;
it --;
if (it->r < pos) return s.end();
int l = it->l, r = it->r, v = it->v;
s.erase(it), s.insert(Node(l, pos-1, v));
return s.insert(Node(pos, r, v)).first;
}
void assign(int l, int r, int x) {
iter itr = split(r+1), itl = split(l);
for (iter it = itl; it != itr; ++it) sum -= (it->r-it->l+1) * it->v;
s.erase(itl, itr), s.insert(Node(l, r, x));
sum += (r-l+1) * x;
}
void div(int l, int r, int x) {
if (!sum) return ;
iter itr = split(r+1), itl = split(l);
for (iter it = itl; it != itr; ++it) sum -= (it->r-it->l+1)*(it->v-it->v/x), it->v /= x;
}
ll query(int l, int r) {
ll tot = 0; iter itr = split(r+1), itl = split(l);
for (iter it = itl; it != itr; ++it) tot += (it->r-it->l+1) * it->v;
return tot;
}
} B[N];
void div(int l, int r, int x) {
int p = block[l], q = block[r];
if (p == q) {
B[p].div(l, r, x);
} else {
B[p].div(l, R[l], x), B[q].div(L[r], r, x);
for (int i = p+1; i <= q-1; ++i) B[i].div(B[i].l, B[i].r, x);
}
}
void assign(int l, int r, int x) {
int p = block[l], q = block[r];
if (p == q) {
B[p].assign(l, r, x);
} else {
B[p].assign(l, R[l], x), B[q].assign(L[r], r, x);
for (int i = p+1; i <= q-1; ++i) B[i].assign(B[i].l, B[i].r, x);
}
}
ll query(int l, int r) {
int p = block[l], q = block[r]; ll sum = 0;
if (p == q) {
return B[p].query(l, r);
} else {
sum += B[p].query(l, R[l]) + B[q].query(L[r], r);
for (int i = p+1; i <= q-1; ++i) sum += B[i].sum;
return sum;
}
}
int main() {
scanf("%d%d", &n, &m); p = sqrt(n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
block[i] = (i+p-1) / p, L[i] = (block[i]-1)*p+1, R[i] = min(block[i]*p, n);
B[block[i]].l = L[i], B[block[i]].r = R[i];
B[block[i]].s.insert(Node(i, i, a[i])), B[block[i]].sum += a[i];
}
int op, l, r, x;
while (m -- ) {
scanf("%d%d%d", &op, &l, &r);
if (op == 1) scanf("%d", &x), div(l, r, x);
else if (op == 2) scanf("%d", &x), assign(l, r, x);
else printf("%lld\n", query(l, r));
}
return 0;
}
9.8
补一些之前没做的题。
ABC259Ex. Yet Another Path Counting:根号分治(紫)
显然有两种做法:
- 枚举起始点
和结束点 ,那么有 种路径。时间复杂度 ,其中 为这种颜色的格子数量。 - 枚举起始点
,动态规划计算这个点到这个点右下方所有点的方案数。时间复杂度 。
根号分治,当
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 410, P = 998244353;
int n, ans, a[N][N], f[N][N], fac[N<<1], infac[N<<1];
vector<pii> col[N*N];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (ll)res * a % P;
a = (ll)a * a % P;
b >>= 1;
}
return res;
}
int C(int a, int b) {
return (ll)fac[a] * infac[b] % P * infac[a-b] % P;
}
void solve1(int c) {
for (int i = 0; i < col[c].size(); ++i) {
for (int j = i; j < col[c].size(); ++j) {
int x1 = col[c][i].first, y1 = col[c][i].second, x2 = col[c][j].first, y2 = col[c][j].second;
(ans += C(x2-x1+y2-y1, x2-x1)) %= P;
}
}
}
void solve2(int c) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
f[i][j] = ((a[i][j] == c) + f[i-1][j] + f[i][j-1]) % P;
if (a[i][j] == c) (ans += f[i][j]) %= P;
}
}
}
int main() {
fac[0] = 1, infac[0] = 1;
for (int i = 1; i <= 800; ++i) fac[i] = (ll)fac[i-1]*i%P, infac[i] = power(fac[i], P-2);
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
scanf("%d", &a[i][j]), col[a[i][j]].push_back({i, j});
}
for (int i = 1; i <= n*n; ++i) {
int k = col[i].size(); if (!k) continue;
if (k <= n) solve1(i); else solve2(i);
}
printf("%d\n", ans);
return 0;
}
ABC256G. Black and White Stones:计数 dp(蓝)
令
当我们从
直接 dp 的时间复杂度是
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define int long long
const int N = 1e4+10, P = 998244353;
int n, d, ans, fac[N], infac[N];
struct Matrix {
int a[3][3];
Matrix() {
memset(a, 0, sizeof a);
}
};
Matrix operator*(const Matrix &x, const Matrix &y) {
Matrix z;
for (int k = 1; k <= 2; ++k) {
for (int i = 1; i <= 2; ++i) {
for (int j = 1; j <= 2; ++j)
(z.a[i][j] += x.a[i][k]*y.a[k][j]) %= P;
}
}
return z;
}
void set_base(Matrix &a) {
for (int i = 1; i <= 2; ++i) a.a[i][i] = 1;
}
int C(int a, int b) {
if (b < 0) return 0;
return fac[a] * infac[b] % P * infac[a-b] % P;
}
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) (res *= a) %= P;
(a *= a) %= P;
b >>= 1;
}
return res;
}
Matrix power(Matrix a, int b) {
Matrix res; set_base(res);
while (b) {
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
signed main() {
fac[0] = 1, infac[0] = 1;
for (int i = 1; i <= 10000; ++i) fac[i] = fac[i-1]*i%P, infac[i] = power(fac[i], P-2);
scanf("%lld%lld", &n, &d);
for (int j = 0; j <= d+1; ++j) {
Matrix base, trans; set_base(base);
trans.a[1][1] = C(d-1, j), trans.a[1][2] = C(d-1, j-1), trans.a[2][1] = C(d-1, j-1), trans.a[2][2] = C(d-1, j-2);
trans = power(trans, n), base = base * trans;
(ans += base.a[1][1]+base.a[2][2]) %= P;
}
printf("%lld\n", ans);
return 0;
}
9.9
ABC255D. ±1 Operation 2:排序,二分(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const int N = 2e5+10;
int n, m, a[N], s[N];
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
sort(a+1, a+n+1);
for (int i = 1; i <= n; ++i) s[i] = s[i-1] + a[i];
while (m -- ) {
int x; scanf("%lld", &x);
int pos = upper_bound(a+1, a+n+1, x) - a - 1;
printf("%lld\n", x*pos-s[pos]+s[n]-s[pos]-(n-pos)*x);
}
return 0;
}
ABC255E. Lucky Numbers:枚举,桶(绿)
显然,当
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
using namespace std;
#define int long long
const int N = 1e5+10;
int n, m, s[N], x[15];
int now, ans;
unordered_map<int, int> nums;
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 2; i <= n; ++i) scanf("%lld", &s[i]);
for (int i = 1; i <= m; ++i) scanf("%lld", &x[i]);
for (int i = 1; i <= n; ++i) {
now = s[i] - now;
for (int j = 1; j <= m; ++j) {
nums[(i%2)?x[j]-now:now-x[j]] ++;
ans = max(ans, nums[(i%2)?x[j]-now:now-x[j]]);
}
}
printf("%lld\n", ans);
return 0;
}
ABC255F. Pre-order and In-order:dfs(绿)
具体看代码。时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int n, a[N], b[N], pos[N], L[N], R[N];
int dfs(int al, int ar, int bl, int br) {
if (al > ar || ar < 0 || al < 0) return 0;
int now = pos[a[al]];
if (now < bl || now > br || ar-al != br-bl) puts("-1"), exit(0);
if (now != bl) L[a[al]] = dfs(al+1, now-bl+al, bl, now-1);
if (now != br) R[a[al]] = dfs(now-bl+al+1, ar, now+1, br);
return a[al];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]), pos[b[i]] = i;
if (a[1] != 1) puts("-1"), exit(0);
dfs(1, n, 1, n);
for (int i = 1; i <= n; ++i) printf("%d %d\n", L[i], R[i]);
return 0;
}
ABC255Ex. Range Harvest Query:珂朵莉树,权值线段树,离散化(紫)
珂朵莉树维护每个段最后一次收获的时间即可。对于一个段
时间复杂度通过颜色段均摊证明,为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
#define int long long
const int P = 998244353, inv = 499122177;
int n, q;
struct Node {
int l, r; mutable int v;
Node (int l, int r = 0, int v = 0) : l(l), r(r), v(v) {};
bool operator < (const Node &T) const {
return l < T.l;
}
};
set<Node> s;
auto split(int pos) {
auto it = s.lower_bound(Node(pos));
if (it != s.end() && it->l == pos) return it;
it --; if (it->r < pos) return s.end();
int l = it->l, r = it->r, v = it->v;
s.erase(it), s.insert(Node(l, pos-1, v));
return s.insert(Node(pos, r, v)).first;
}
void assign(int l, int r, int x) {
auto itr = split(r+1), itl = split(l);
s.erase(itl, itr), s.insert(Node(l, r, x));
}
int query(int l, int r, int d) {
int sum = 0; auto itr = split(r+1), itl = split(l);
for (auto it = itl; it != itr; ++it) (sum += (__int128)(d-it->v)%P * ((it->r+it->l)%P) % P * ((it->r-it->l+1)%P) % P * inv % P) %= P;
return sum;
}
signed main() {
scanf("%lld%lld", &n, &q); s.insert(Node(1, n, 0));
int d, l, r;
while (q -- ) {
scanf("%lld%lld%lld", &d, &l, &r);
printf("%lld\n", query(l, r, d)), assign(l, r, d);
}
return 0;
}
ABC254D. Together Square:线性筛,数学(黄)
对于每一个数
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 2e5+10;
int n, ans;
int idx, prime[N]; bool st[N];
void get_prime() {
st[0] = st[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!st[i]) prime[++ idx] = i;
for (int j = 1; j <= idx && i*prime[j] <= n; ++j) {
st[i*prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int main() {
scanf("%d", &n); get_prime();
for (int i = 1; i <= n; ++i) {
int now = 1;
if (!st[i] || i == 1) now = i;
else {
int t = i;
for (int j = 1; j <= idx && t > 1; ++j) {
int cnt = 0;
while (t % prime[j] == 0) t /= prime[j], cnt ++;
if (cnt & 1) now *= prime[j];
}
}
ans += sqrt(n/now);
}
printf("%d\n", ans);
return 0;
}
ABC254E. Small d and k:dfs/bfs(黄)
由于保证了每个点的度数
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int N = 2e5+10;
int n, m, q; bool st[N];
vector<int> e[N];
int dfs(int u, int k) {
if (!k) {
if (st[u]) return 0;
return st[u] = 1, u;
}
int tot = (st[u]) ? 0 : u; st[u] = 1;
for (auto v : e[u]) tot += dfs(v, k-1);
return tot;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
scanf("%d", &q);
while (q -- ) {
memset(st, 0, sizeof st);
int x, k; scanf("%d%d", &x, &k);
printf("%d\n", dfs(x, k));
}
return 0;
}
ABC254F. Rectangle GCD:数学,st 表(绿)
先考虑同一行的情况。我们有:
同样地,对于同一行的情况,我们也有类似的结论。那么整个子矩阵的 gcd 即为:
我们只需要用 st 表维护差分数组的区间 gcd 即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 2e5+10;
int n, m, a[N][3], c[N][3], st[N][30][3];
int query(int l, int r, int op) {
int k = log2(r-l+1);
return __gcd(st[l][k][op], st[r-(1<<k)+1][k][op]);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= 2; ++i)
for (int j = 1; j <= n; ++j)
scanf("%d", &a[j][i]), c[j][i] = abs(a[j][i]-a[j-1][i]), st[j][0][i] = c[j][i];
for (int i = 1; i <= 2; ++i) {
for (int k = 1; (1<<k) <= n; ++k)
for (int j = 1; j+(1<<k)-1 <= n; ++j)
st[j][k][i] = __gcd(st[j][k-1][i], st[j+(1<<k-1)][k-1][i]);
}
while (m -- ) {
int x1, y1, x2, y2; scanf("%d%d%d%d", &x1, &x2, &y1, &y2);
int ans = a[x1][1]+a[y1][2];
if (x1+1 <= x2) ans = __gcd(ans, query(x1+1, x2, 1));
if (y1+1 <= y2) ans = __gcd(ans, query(y1+1, y2, 2));
printf("%d\n", ans);
}
return 0;
}
9.10
补昨晚 ABC 319:
ABC319A. Legendary Players:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
unordered_map<string, int> S;
int main() {
S["tourist"] = 3858, S["ksun48"] = 3679, S["Benq"] = 3658, S["Um_nik"] = 3648, S["apiad"] = 3638, S["Stonefeang"] = 3630, S["ecnerwala"] = 3613, S["mnbvmar"] = 3555, S["newbiedmy"] = 3516, S["semiexp"] = 3481;
string s; cin >> s; cout << S[s] << '\n';
return 0;
}
ABC319B. Measure:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int n;
int main() {
cin >> n;
for (int i = 0; i <= n; ++i) {
int j;
for (j = 1; j <= 9; ++j) if (n % j == 0 && (i % (n/j) == 0)) break;
cout << (char)((j == 10) ? '-' : j+'0');
}
return 0;
}
ABC319C. False Hope:枚举(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int a[5][5], nums[10], tot, sum;
int main() {
for (int i = 1; i <= 3; ++i) {
for (int j = 1; j <= 3; ++j)
scanf("%d", &a[i][j]);
}
for (int i = 1; i <= 9; ++i) nums[i] = i;
do {
sum ++; bool check = 1;
vector<pii> line[10];
for (int i = 1; i <= 9; ++i) {
int x = (nums[i]-1)/3+1, y = (nums[i]%3)?nums[i]%3:3;
line[x].push_back({x, y}), line[y+3].push_back({x, y});
if (x == y) line[7].push_back({x, y});
if (x+y == 4) line[8].push_back({x, y});
}
for (int i = 1; i <= 8; ++i) {
int x1 = line[i][0].first, y1 = line[i][0].second, x2 = line[i][1].first, y2 = line[i][1].second;
if (a[x1][y1] == a[x2][y2]) {check = 0; break;}
}
if (check) tot ++;
} while (next_permutation(nums+1, nums+10));
printf("%.10f\n", (double)tot/sum);
return 0;
}
ABC319D. Minimum Width:二分(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
#define int long long
const int N = 2e5+10;
int n, m, maxl = 0, a[N];
bool check(int x) {
int cnt = 1, now = 0;
for (int i = 1; i <= n; ++i) {
if (now+a[i] <= x) now += a[i]+1;
else now = a[i]+1, cnt ++;
}
return cnt <= m;
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]), maxl = max(maxl, a[i]);
int l = maxl, r = 1e17;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid+1;
}
printf("%lld\n", l);
return 0;
}
ABC319E. Bus Stops:数学,枚举(绿)
题目中保证了
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
const int N = 1e5+10, M = 840;
#define int long long
int n, m, x, y, p[N], t[N], ans[M];
signed main() {
scanf("%lld%lld%lld", &n, &x, &y);
for (int i = 1; i < n; ++i) scanf("%lld%lld", &p[i], &t[i]);
for (int i = 0; i < 840; ++i) {
ans[i] += x+i;
for (int j = 1; j < n; ++j) {
if (ans[i] % p[j]) ans[i] += p[j] - ans[i]%p[j];
ans[i] += t[j];
}
ans[i] += y;
}
scanf("%lld", &m);
while (m -- ) {
int q; scanf("%lld", &q);
printf("%lld\n", ans[q%M]+q/M*M);
}
return 0;
}
9.11
ABC319G. Counting Shortest Paths:最短路,bfs,dp(蓝)
考虑对于每个点求出其与 set
和 queue
实现,其中 set
维护当前还没有走到的点。每次我们在 set
里找到可以扩展的点并将它们从 set
中删除并加入队列。
统计答案时,我们考虑 dp。令
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <set>
using namespace std;
const int N = 2e5+10, P = 998244353;
int n, m, dist[N], f[N], s[N];
vector<int> e[N];
set<int> S, p[N];
void bfs() {
queue<int> q; q.push(1); dist[1] = 1;
while (q.size()) {
int u = q.front(); q.pop(); set<int> SS = S;
for (auto v : e[u]) {
if (SS.find(v) != S.end())
SS.erase(v);
}
for (auto v : SS) S.erase(v), dist[v] = dist[u]+1, q.push(v);
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
for (int i = 2; i <= n; ++i) S.insert(i);
bfs();
if (!dist[n]) return puts("-1"), 0;
f[1] = 1, s[1] = 1;
for (int i = 2; i <= n; ++i) p[dist[i]].insert(i);
for (int i = 2; i <= dist[n]; ++i) {
for (auto u : p[i]) {
f[u] = s[i-1];
for (auto v : e[u]) {
if (dist[v] == dist[u]-1)
(f[u] += P-f[v]) %= P;
}
(s[i] += f[u]) %= P;
}
}
printf("%d\n", f[n]);
return 0;
}
ABC254Ex. Multiply or Divide by 2:堆,贪心(紫)
我们记当前
:那么我们需要将 除以 ; :此时,若 为奇数,则不可能完成。否则,我们需要将 乘上 ,也就相当于将对应的 除以 。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 1e5+10;
int n, ans, a[N], b[N];
priority_queue<int> A, B;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++i) A.push(a[i]), B.push(b[i]);
while (A.size() && B.size()) {
int x = A.top(), y = B.top(); A.pop(), B.pop();
if (x > y) {
x >>= 1; A.push(x), B.push(y);
ans ++;
} else if (x < y) {
if (y & 1) return puts("-1"), 0;
y >>= 1; A.push(x), B.push(y);
ans ++;
}
}
printf("%d\n", ans);
return 0;
}
ABC253D. FizzBuzz Sum Hard:数学(橙)
显然答案为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
int n, a, b, c, ans;
signed main() {
cin >> n >> a >> b; c = a*b / __gcd(a, b);
ans = n*(n+1)/2 - a*(n/a+1)*(n/a)/2 - b*(n/b+1)*(n/b)/2 + c*(n/c+1)*(n/c)/2;
cout << ans << '\n';
return 0;
}
ABC253E. Distance Sequence:dp(黄)
令
前缀和优化即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1010, M = 5010, P = 998244353;
int n, m, k, ans, f[N][M], s[N][M];
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= m; ++i) f[1][i] = 1, s[1][i] = s[1][i-1]+f[1][i];
for (int i = 2; i <= n; ++i) {
for (int j = 1; j <= m; ++j) {
(f[i][j] = ((long long)s[i-1][m] - (k?(s[i-1][min(m, j+k-1)]-s[i-1][max(0, j-k)]):0)) % P + P) %= P;
(s[i][j] = s[i][j-1]+f[i][j]) %= P;
}
}
printf("%d\n", s[n][m]);
return 0;
}
9.12
ABC253F. Operations on a Matrix:树状数组,可持久化线段树(绿)
对于操作编号为
代码有点抽象。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
#define int long long
const int N = 2e5+10;
int n, m, t, last[N], c[N];
int idx, ans[N];
struct Node {
int op, a, b, c;
} q[N];
vector<Node> mf[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, int k) {
for (int i = x; i <= m; i += lowbit(i)) c[i] += k;
}
int query(int x) {
int res = 0;
for (int i = x; i >= 1; i -= lowbit(i)) res += c[i];
return res;
}
signed main() {
scanf("%lld%lld%lld", &n, &m, &t);
int op, a, b, c;
for (int i = 1; i <= t; ++i) {
scanf("%lld%lld%lld", &op, &a, &b);
if (op == 1) scanf("%lld", &c);
else if (op == 2) last[a] = i; // last[a]存储第i行上一次修改的操作编号
else ans[++ idx] = q[last[a]].b, mf[last[a]].push_back({-1, b, idx}), mf[i].push_back({1, b, idx}); // 表示对于第idx次查询操作, 需要减去第last[a]次操作之前增加的量, 并加上第i次操作之前增加的量
q[i] = {op, a, b, c};
}
for (int i = 1; i <= t; ++i) {
op = q[i].op, a = q[i].a, b = q[i].b, c = q[i].c;
if (op == 1) add(a, c), add(b+1, -c);
for (auto p : mf[i]) {
int f = p.op, c = p.a, pos = p.b;
ans[pos] += f * query(c);
}
}
for (int i = 1; i <= idx; ++i) printf("%lld\n", ans[i]);
return 0;
}
ABC253G. Swap Many Times:分块,枚举(蓝)
考虑如果我们将操作
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
#define int long long
int n, l, r, a[N], b[N], siz[N];
void modify(int l, int r) {
for (int i = l; i <= r; ++i) {
int x = lower_bound(siz+1, siz+n+1, i)-siz, y = i-(n*(x-1)-x*(x-1)/2)+x;
swap(a[x], a[y]);
}
}
signed main() {
scanf("%lld%lld%lld", &n, &l, &r);
for (int i = 1; i <= n; ++i) a[i] = i;
for (int i = 1; i <= n; ++i) siz[i] = siz[i-1] + n-i;
int p = lower_bound(siz+1, siz+n+1, l)-siz, q = lower_bound(siz+1, siz+n+1, r)-siz;
if (q-p < 2) modify(l, r);
else {
modify(l, siz[p]);
int idx = 0;
for (int i = 1; i <= p; ++i) b[++ idx] = a[i];
for (int i = n; i >= n-q+p+2; --i) b[++ idx] = a[i];
for (int i = p+1; i < n-q+p+2; ++i) b[++ idx] = a[i];
for (int i = 1; i <= n; ++i) a[i] = b[i];
modify(siz[q-1]+1, r);
}
for (int i = 1; i <= n; ++i) printf("%lld ", a[i]);
return 0;
}
ABC252D. Distinct Trio:枚举,桶(黄)
我们可以转换一下题意:将原数组排序后,满足
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n, cnt, a[N], c[N]; ll ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), c[a[i]] ++;
sort(a+1, a+n+1);
for (int i = 1; i <= n; ++i) {
if (a[i] != a[i-1]) cnt = 1; else cnt ++;
ans += (ll)(i-cnt) * (n-(i-cnt+c[a[i]]));
}
printf("%lld\n", ans);
return 0;
}
ABC252E. Road Reduction:最短路树(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5+10;
struct Edge {
int v, w, id;
};
int n, m, p[N], dist[N]; bool st[N];
vector<Edge> e[N];
void dijkstra() {
memset(dist, 0x7f, sizeof dist);
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, 1}), dist[1] = 0;
while (q.size()) {
auto t = q.top(); q.pop();
int u = t.second, dis = t.first;
if (st[u]) continue;
st[u] = 1;
for (auto edge : e[u]) {
int v = edge.v, w = edge.w, id = edge.id;
if (dist[v] > dis+w) dist[v] = dis+w, p[v] = id, q.push({dist[v], v});
}
}
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v, w; scanf("%lld%lld%lld", &u, &v, &w);
e[u].push_back({v, w, i}), e[v].push_back({u, w, i});
}
dijkstra();
for (int i = 2; i <= n; ++i) printf("%lld ", p[i]);
return 0;
}
ABC252F. Bread:堆,贪心。
合并果子,注意若
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
#define int long long
int n, l, tot, ans;
priority_queue<int, vector<int>, greater<int>> q;
signed main() {
scanf("%lld%lld", &n, &l);
for (int i = 1; i <= n; ++i) {
int x; scanf("%lld", &x);
q.push(x); tot += x;
}
if (l > tot) q.push(l-tot);
while (q.size() > 1) {
int a = q.top(); q.pop();
int b = q.top(); q.pop();
ans += a+b, q.push(a+b);
}
printf("%lld\n", ans);
return 0;
}
9.16
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <iomanip>
#include <cmath>
using namespace std;
int a, b;
int main() {
cin >> a >> b;
cout << fixed << setprecision(0) << pow(a, b)+pow(b, a) << '\n';
return 0;
}
ABC320B. Longest Palindrome:枚举,manacher(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3e7+10;
int n, idx, ans, d[N];
char S[N], T[N];
void build() {
S[++ idx] = '!', S[++ idx] = '#';
for (int i = 1; i <= n; ++i) S[++ idx] = T[i], S[++ idx] = '#';
S[++ idx] = '^';
}
void get_d() {
for (int i = 2, l = 0, r = 0; i < idx; ++i) {
int j = l + r - i;
if (i <= r) d[i] = min(d[j], r-i+1);
while (S[i+d[i]] == S[i-d[i]]) d[i] ++;
if (i+d[i] > r) r = i+d[i]-1, l = i-d[i]+1;
ans = max(ans, d[i]-1);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> T+1;
n = strlen(T+1);
build(), get_d();
cout << ans << '\n';
return 0;
}
ABC320C. Slot Strategy 2 (Easy):模拟,枚举(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 110, M = 3e5+10;
int n = 3, m, ne[N][M], cnt[15]; char s[N][M];
bool st[N][15];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> m;
for (int i = 1; i <= n; ++i) cin >> s[i]+1;
for (int i = 1; i <= n; ++i) {
for (int j = m+1; j <= 3*m; ++j)
s[i][j] = s[i][(j-1)%m+1];
}
m *= 3;
for (int i = 0; i <= 9; ++i) cnt[i] = n;
for (int i = 1; i <= n; ++i) {
int now[15]; memset(now, 0, sizeof now);
for (int j = m; j >= 1; --j) {
int x = s[i][j] - '0';
if (!now[x]) ne[i][j] = m+1;
else ne[i][j] = now[x];
now[x] = j;
}
}
for (int i = 1; i <= m; ++i) {
vector<int> nums[15];
for (int j = 1; j <= n; ++j) {
int x = s[j][i] - '0';
if (!st[j][x]) nums[x].push_back(j);
}
for (int j = 0; j <= 9; ++j) {
int maxpos = 0, maxr = 0;
for (auto k : nums[j]) if (ne[k][i] > maxpos) maxpos = ne[k][i], maxr = k;
if (!maxr) continue;
st[maxr][j] = 1, cnt[j] --;
if (!cnt[j]) return cout << i-1 << '\n', 0;
}
}
return cout << -1 << '\n', 0;
}
ABC320D. Relative Position:dfs(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
#define int long long
typedef pair<int, int> pii;
#define x first
#define y second
const int N = 2e5+10;
int n, m; pii pos[N];
bool st[N], circ[N];
struct Edge {
int v, x, y;
};
vector<Edge> e[N];
void dfs(int u) {
st[u] = 1;
for (auto edge : e[u]) {
int v = edge.v, x = edge.x, y = edge.y;
if (st[v]) {
int X = pos[v].x, Y = pos[v].y;
if (X != pos[u].x+x || Y != pos[u].y+y) circ[v] = 1;
continue;
}
pos[v].x = pos[u].x+x, pos[v].y = pos[u].y+y;
dfs(v);
}
}
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= m; ++i) {
int a, b, x, y; scanf("%lld%lld%lld%lld", &a, &b, &x, &y);
e[a].push_back({b, x, y}), e[b].push_back({a, -x, -y});
}
pos[1] = {0, 0}; dfs(1);
for (int i = 1; i <= n; ++i) {
if (!st[i] || circ[i]) puts("undecidable");
else printf("%lld %lld\n", pos[i].x, pos[i].y);
}
return 0;
}
ABC320E. Somen Nagashi:模拟,堆(黄)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2e5+10;
#define int long long
typedef pair<int, int> pii;
int n, m, ans[N];
priority_queue<int, vector<int>, greater<int>> Q;
priority_queue<pii, vector<pii>, greater<pii>> q;
signed main() {
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; ++i) Q.push(i);
for (int i = 1; i <= m; ++i) {
int t, w, s; scanf("%lld%lld%lld", &t, &w, &s);
while (q.size() && q.top().first <= t) Q.push(q.top().second), q.pop();
if (!Q.size()) continue;
ans[Q.top()] += w, q.push({t+s, Q.top()}), Q.pop();
}
for (int i = 1; i <= n; ++i) printf("%lld\n", ans[i]);
return 0;
}
9.20
ABC251D. At Most 3 (Contestant ver.):思维(橙)
由于
- 第一组:
; - 第二组:
; - 第三组:
。
这样就可以用不超过
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int w;
int main() {
scanf("%d", &w);
puts("300");
for (int i = 1; i <= 100; ++i) printf("%d %d %d ", i, i*100, i*10000);
return 0;
}
ABC251E. Takahashi and Animals:环形 dp(黄)
断环成链,做两次 dp 即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define int long long
const int N = 3e5+10, inf = 8e18;
int n, ans = inf, a[N<<1], f[N][2];
signed main() {
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) scanf("%lld", &a[i]);
for (int i = n+1; i <= 2*n; ++i) a[i] = a[i-n];
memset(f, 0x7f, sizeof f), f[1][1] = a[1];
for (int i = 2; i <= n; ++i) f[i][0] = f[i-1][1], f[i][1] = min(f[i-1][0], f[i-1][1])+a[i];
ans = min(ans, min(f[n][0], f[n][1]));
memset(f, 0x7f, sizeof f), f[n][1] = a[n];
for (int i = n+1; i <= 2*n-1; ++i) f[i][0] = f[i-1][1], f[i][1] = min(f[i-1][0], f[i-1][1])+a[i];
ans = min(ans, min(f[2*n-1][0], f[2*n-1][1]));
printf("%lld\n", ans);
return 0;
}
ABC251F. Two Spanning Trees:dfs,bfs(绿)
先给出结论:
证明:
对于
对于
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int N = 2e5+10;
int n, m, dist[N]; bool st[N];
vector<int> e[N], d[N];
void dfs(int u) {
st[u] = 1;
for (auto v : e[u]) {
if (st[v]) continue;
printf("%d %d\n", u, v), dfs(v);
}
}
void bfs() {
queue<int> q; q.push(1), dist[1] = 1, d[1].push_back(1);
while (q.size()) {
int u = q.front(); q.pop();
for(auto v : e[u]) {
if (dist[v]) continue;
dist[v] = dist[u]+1, q.push(v), d[dist[v]].push_back(v);
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
dfs(1);
bfs(); memset(st, 0, sizeof st);
for (int i = 1; i <= n; ++i) {
if (!d[i].size()) break;
for (auto u : d[i]) {
for (auto v : e[u])
if (dist[v] == dist[u]+1 && !st[v])
printf("%d %d\n", u, v), st[v] = 1;
}
}
return 0;
}
ABC250D. 250-like Number:数学(黄)
显然,
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
ll n, ans;
int idx, prime[N], num[N]; bool st[N];
void get_prime(int n) {
st[0] = st[1] = 1;
for (int i = 2; i <= n; ++i) {
num[i] += num[i-1];
if (!st[i]) prime[++ idx] = i, num[i] ++;
for (int j = 1; j <= idx && prime[j] <= n/i; ++j) {
st[i*prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
int main() {
scanf("%lld", &n);
get_prime(pow(n, 1.0/3));
for (int i = 1; i <= idx; ++i) ans += num[min((ll)(n/pow(prime[i], 3)), (ll)prime[i]-1)];
printf("%lld\n", ans);
return 0;
}
ABC250E. Prefix Equality:哈希(绿)
由于左端点固定,还是比较好哈希的。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
using namespace std;
typedef unsigned long long ull;
const int N = 2e5+10;
int n, m, a[N], b[N];
map<int, int> numA, numB; ull ha[N], hb[N];
ull power(ull a, ull b) {
ull res = 1;
while (b) {
if (b & 1) res *= a;
a *= a, b >>= 1;
}
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++i) {
if (!numA[a[i]]) numA[a[i]] = 1, ha[i] = ha[i-1]+power(a[i], 3);
else ha[i] = ha[i-1];
if (!numB[b[i]]) numB[b[i]] = 1, hb[i] = hb[i-1]+power(b[i], 3);
else hb[i] = hb[i-1];
}
scanf("%d", &m);
while (m -- ) {
int x, y; scanf("%d%d", &x, &y);
(ha[x] == hb[y]) ? puts("Yes") : puts("No");
}
return 0;
}
ABC250G. Stonks:反悔贪心(蓝)(双倍经验:CF865D Buy Low Sell High)
考虑用一个堆存储前面每天的股票价值。每一天,如果堆不空,我们就找到之前价格最低的一天买入股票然后卖出。这个贪心很显然是假的。考虑怎么反悔。
如果我们在第
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 3e5+10;
int n, a[N]; ll ans;
priority_queue<int, vector<int>, greater<int>> q;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
if (q.size() && q.top() < a[i]) ans += (ll)a[i]-q.top(), q.pop(), q.push(a[i]);
q.push(a[i]);
}
printf("%lld\n", ans);
return 0;
}
9.21
ABC250Ex. Trespassing Takahashi:最短路,并查集(紫)
考虑初始时将所有房子都加入优先队列中,然后跑一遍 dijkstra,令
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5+10;
int n, m, k, q; vector<pii> e[N];
int dist[N], p[N]; bool st[N];
struct Edge {
int u, v, w;
bool operator < (const Edge &T) const {
return w < T.w;
}
} edge[N];
void dijkstra() {
memset(dist, 0x3f, sizeof dist);
priority_queue<pii, vector<pii>, greater<pii>> q;
for (int i = 1; i <= k; ++i) q.push({0, i}), dist[i] = 0;
while (q.size()) {
int u = q.top().second; q.pop();
if (st[u]) continue;
st[u] = 1;
for (pii edg : e[u]) {
int v = edg.first, w = edg.second;
if (dist[v] > dist[u]+w) dist[v] = dist[u]+w, q.push({dist[v], v});
}
}
}
int find(int x) {
return (p[x] == x) ? x : p[x] = find(p[x]);
}
void merge(int u, int v) {
int fu = find(u), fv = find(v);
if (fu == fv) return ;
p[fu] = fv;
}
signed main() {
scanf("%lld%lld%lld", &n, &m, &k);
for (int i = 1; i <= m; ++i) {
int u, v, w; scanf("%lld%lld%lld", &u, &v, &w);
e[u].push_back({v, w}), e[v].push_back({u, w}), edge[i] = {u, v, w};
}
dijkstra();
for (int i = 1; i <= n; ++i) p[i] = i;
for (int i = 1; i <= m; ++i) edge[i].w += dist[edge[i].u] + dist[edge[i].v];
sort(edge+1, edge+m+1);
int now = 1; scanf("%lld", &q);
while (q -- ) {
int u, v, t; scanf("%lld%lld%lld", &u, &v, &t);
while (edge[now].w <= t && now <= m) merge(edge[now].u, edge[now].v), now ++;
if (find(u) == find(v)) puts("Yes");
else puts("No");
}
return 0;
}
ABC249D. Index Trio:枚举,桶(黄)
我们用一个桶记录
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10, M = 2e5;
int n, a[N], nums[N]; long long ans;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), nums[a[i]] ++;
for (int i = 1; i <= M; ++i) {
for (int j = 1; j <= M/i; ++j)
ans += (long long)nums[i] * nums[j] * nums[i*j];
}
printf("%lld\n", ans);
return 0;
}
ABC249E. RLE:dp(蓝)
令
注意到
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long
const int N = 3010;
int n, P, ans, f[N][N], s[N][N], p[N];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = res * a % P;
a = a * a % P, b >>= 1;
}
return res;
}
signed main() {
scanf("%lld%lld", &n, &P);
p[0] = 1; for (int i = 1; i <= 4; ++i) p[i] = p[i-1] * 10;
f[0][0] = s[0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 1; k <= 4; ++k) {
int l = i-p[k]+1, r = i-p[k-1], pos = j-k-1;
if (l > r || r < 0 || pos < 0) continue;
(f[i][j] += 25ll*(s[r][pos]-(l>0?s[l-1][pos]:0)+P)) %= P;
}
(s[i][j] = s[i-1][j] + f[i][j]) %= P;
}
}
for (int j = 0; j < n; ++j) (ans += f[n][j]) %= P;
ans = ans * power(25, P-2) % P * 26 % P;
printf("%lld\n", ans);
return 0;
}
9.25
ABC248D. Range Count Query:二分(黄)
值域很小,每个数用一个 vector
存即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 2e5+10;
int n, m, a[N];
vector<int> nums[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), nums[a[i]].push_back(i);
scanf("%d", &m);
while (m -- ) {
int l, r, x; scanf("%d%d%d", &l, &r, &x);
int posl = lower_bound(nums[x].begin(), nums[x].end(), l) - nums[x].begin(), posr = upper_bound(nums[x].begin(), nums[x].end(), r) - nums[x].begin() - 1;
printf("%d\n", max(0, posr-posl+1));
}
return 0;
}
ABC248E. K-colinear Line:数学,枚举(黄)
显然,Infinity
。
我们知道,两点确定一条直线。而如何判断第三个点是否在这条直线上呢?直接用解析式算会有误差,设三个点分别为
交叉相乘即可。
去重可以取每条直线的起始点和终止点,用 set
去重。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
#define int long long
typedef pair<int, int> pii;
#define x first
#define y second
const int N = 310;
int n, t; pii dots[N];
set<pii> s;
bool check(int i, int j, int k) {
if (dots[i].x == dots[j].x && dots[j].x == dots[k].x) return 1;
if (dots[i].y == dots[j].y && dots[j].y == dots[k].y) return 1;
if ((dots[i].x-dots[j].x)*(dots[j].y-dots[k].y) == (dots[j].x-dots[k].x)*(dots[i].y-dots[j].y)) return 1;
return 0;
}
signed main() {
scanf("%lld%lld", &n, &t);
for (int i = 1; i <= n; ++i) scanf("%lld%lld", &dots[i].x, &dots[i].y);
if (t == 1) puts("Infinity"), exit(0);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (j == i) continue;
int cnt = 2, begin = min(i, j), last = max(i, j);
for (int k = 1; k <= n; ++k) {
if (k != i && k != j && check(i, j, k))
cnt ++, begin = min(begin, k), last = max(last, k);
}
if (cnt >= t) s.insert({begin, last});
}
}
printf("%lld\n", (int)s.size());
return 0;
}
ABC248F. Keep Connect:dp(绿)
令
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 3010;
int n, p, f[N][N][2];
int main() {
scanf("%d%d", &n, &p);
f[1][0][1] = f[1][1][0] = 1;
for (int i = 1; i < n; ++i) {
for (int j = 0; j < n; ++j) {
(f[i+1][j+2][0] += 2ll*f[i][j][1]%p) %= p;
(f[i+1][j+1][1] += 3ll*f[i][j][1]%p) %= p;
(f[i+1][j][1] += f[i][j][1]) %= p;
(f[i+1][j+1][0] += f[i][j][0]) %= p;
(f[i+1][j][1] += f[i][j][0]) %= p;
}
}
for (int j = 1; j < n; ++j) printf("%d ", f[n][j][1]); puts("");
return 0;
}
ABC248G. GCD cost on the tree:树形 dp,数学(紫)
考虑枚举路径上所有点的 gcd 为
对于边
,这是显然的。 ,这是因为 中所有以 为一端点的链在接到 上时,长度都要增加 ,所以 要再加上 。 ,这是因为之前以 为一端点的链有 条,将它们都接到 上就会产生 的贡献,后面的部分同理。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 100001, P = 998244353;
int n, m, a[N]; vector<int> e[N], g[N];
int cnt[N], len[N], res[N], ans[N]; bool vis[N];
void dfs(int u, int x) {
cnt[u] = 1, len[u] = 1, res[u] = 0, vis[u] = 0;
for (auto v : e[u]) {
if (!vis[v]) continue; dfs(v, x);
(res[u] += (1ll*cnt[u]*len[v]+1ll*cnt[v]*len[u])%P) %= P;
(cnt[u] += cnt[v]) %= P;
(len[u] += len[v]+cnt[v]) %= P;
}
(ans[x] += res[u]) %= P;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i < n; ++i) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v), e[v].push_back(u);
}
for (int i = 1; i <= n; ++i) {
int x = a[i];
for (int j = 1; j <= x/j; ++j) {
if (x % j) continue;
g[j].push_back(i);
if (j != x/j) g[x/j].push_back(i);
}
}
for (int i = N-1; i > 0; --i) {
if (!g[i].size()) continue;
for (auto j : g[i]) vis[j] = 1;
for (auto j : g[i]) if (vis[j]) dfs(j, i);
for (int j = i+i; j < N; j += i) (ans[i] += P-ans[j]) %= P;
}
int sum = 0;
for (int i = 1; i < N; ++i) (sum += 1ll*ans[i]*i%P) %= P;
printf("%d\n", sum);
return 0;
}
9.26
ABC248Ex. Beautiful Subsequences:分治,扫描线(黑)
用分治的方法,令
我们首先预处理出区间
均在左侧:枚举 ,则 。若合法则可以对答案造成 的贡献。 在右侧, 在左侧:同样枚举 ,双指针统计右端点的范围,且 。用一个桶统计即可。
其余两种情况是类似的。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int n, K, a[N]; long long ans;
int mx[N], mn[N], cnt[N<<2];
void solve(int L, int R) {
if (L == R) {ans ++; return ;}
int mid = L + R >> 1; solve(L, mid), solve(mid+1, R);
mx[mid] = mn[mid] = a[mid], mx[mid+1] = mn[mid+1] = a[mid+1];
for (int i = mid-1; i >= L; --i) mx[i] = max(mx[i+1], a[i]), mn[i] = min(mn[i+1], a[i]); // suffix max/min of the left
for (int i = mid+2; i <= R; ++i) mx[i] = max(mx[i-1], a[i]), mn[i] = min(mn[i-1], a[i]); // prefix max/min of the right
// min: left, max: left
for (int i = mid; i >= L; --i) {
for (int k = 0; k <= K; ++k) {
int r = mx[i] - mn[i] + i - k;
if (r > mid && r <= R && mx[i] > mx[r] && mn[i] < mn[r]) ans ++;
}
}
// min: right, max: left
for (int i = mid, r = mid+1, rr = mid+1; i >= L; --i) {
while (mx[r] < mx[i] && r <= R) cnt[r+mn[r]+n] ++, r ++;
while (mn[rr] > mn[i] && rr < r) cnt[rr+mn[rr]+n] --, rr ++;
for (int k = 0; k <= K; ++k) ans += cnt[i+mx[i]-k+n];
}
// min: left, max: right
for (int i = mid+1; i <= R; ++i) cnt[i+mn[i]+n] = 0;
for (int i = mid+1, l = mid, ll = mid; i <= R; ++i) {
while (mx[l] < mx[i] && l >= L) cnt[l-mn[l]+n] ++, l --;
while (mn[ll] > mn[i] && ll > l) cnt[ll-mn[ll]+n] --, ll --;
for (int k = 0; k <= K; ++k) ans += cnt[i-mx[i]+k+n];
}
// min: right, max: right
for (int i = L; i <= mid; ++i) cnt[i-mn[i]+n] = 0;
for (int i = mid+1; i <= R; ++i) {
for (int k = 0; k <= K; ++k) {
int l = i + k - mx[i] + mn[i];
if (l <= mid && l >= L && mx[l] < mx[i] && mn[l] > mn[i]) ans ++;
}
}
}
int main() {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
solve(1, n);
printf("%lld\n", ans);
return 0;
}
ABC247D. Cylinder:模拟,队列(橙)
直接用 deque
模拟即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <deque>
using namespace std;
#define int long long
typedef pair<int, int> pii;
const int N = 2e5+10;
int q, idx, tot;
deque<pii> Q;
signed main() {
scanf("%lld", &q);
int op, c, x;
while (q -- ) {
scanf("%lld%lld", &op, &c);
if (op == 1) scanf("%lld", &x), swap(x, c), Q.push_back({x, c});
else {
int tot = 0;
while (c) {
auto t = Q.front(); Q.pop_front(); int num = t.first, cnt = t.second;
if (cnt <= c) tot += num*cnt, c -= cnt;
else tot += num*c, Q.push_front({num, cnt-c}), c = 0;
}
printf("%lld\n", tot);
}
}
return 0;
}
9.27
ABC247E. Max Min:容斥原理,双指针,枚举(黄)
我们令
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int n, x, y, a[N];
long long f(int y, int x) {
long long cnt = 0;
for (int r = 1, l = 1; r <= n; ++r) {
if (a[r] < y || a[r] > x) l = r+1;
cnt += r-l+1;
}
return cnt;
}
int main() {
scanf("%d%d%d", &n, &x, &y);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
printf("%lld\n", f(y, x)-f(y+1, x)-f(y, x-1)+f(y+1, x-1));
return 0;
}
ABC247F. Cards:dp,并查集(绿)
我们从
可以用并查集维护每个环的大小。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10, P = 998244353;
int n, ans = 1, a[N], b[N], p[N], siz[N], f[N];
int find(int x) {
return p[x] = (p[x] == x) ? x : find(p[x]);
}
void merge(int u, int v) {
int fu = find(u), fv = find(v);
if (fu == fv) return ;
p[fu] = fv, siz[fv] += siz[fu];
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) p[i] = i, siz[i] = 1;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]);
for (int i = 1; i <= n; ++i) merge(a[i], b[i]);
f[1] = 1, f[2] = 3;
for (int i = 3; i <= n; ++i) (f[i] += f[i-1]+f[i-2]) %= P;
for (int i = 1; i <= n; ++i) {
if (find(i) != i) continue;
ans = (long long)ans * f[siz[i]] % P;
}
printf("%d\n", ans);
return 0;
}
ABC246D. 2-variable Function:枚举,二分(黄)
枚举
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
int n, ans = 1e18;
int get(int x) {
int l = x, r = 1e8;
while (l < r) {
int mid = l + r >> 1;
if ((x+mid)*((__int128)x*x+mid*mid) >= n) r = mid;
else l = mid+1;
}
return (x+l)*(x*x+l*l);
}
signed main() {
cin >> n;
for (int i = 0; i <= 1000000; ++i) ans = min(ans, get(i));
cout << ans << '\n';
return 0;
}
ABC246E. Bishop 2:bfs(绿)
注意实现。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
const int N = 1510;
const int dx[] = {-1, -1, 1, 1}, dy[] = {-1, 1, -1, 1};
int n; pii S, T;
char s[N][N]; int dist[N][N];
void bfs() {
queue<pii> q; q.push(S);
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) dist[i][j] = 1000000000;
dist[S.first][S.second] = 0;
while (q.size()) {
pii t = q.front(); q.pop();
if (t == T) return ;
int x = t.first, y = t.second;
for (int i = 0; i < 4; ++i) {
int X = x+dx[i], Y = y+dy[i];
while (X && Y && X <= n && Y <= n && dist[X][Y] > dist[x][y] && s[X][Y] != '#') dist[X][Y] = dist[x][y]+1, q.push({X, Y}), X += dx[i], Y += dy[i];
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> S.first >> S.second >> T.first >> T.second;
for (int i = 1; i <= n; ++i) cin >> s[i]+1;
bfs();
if (dist[T.first][T.second] != 1000000000) printf("%d\n", dist[T.first][T.second]);
else puts("-1");
return 0;
}
ABC246F. typewriter:容斥原理,数学(绿)
在不考虑重复的情况下,一个字符集能组成的单词数显然为
bitset 优化计算并集,时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <bitset>
using namespace std;
typedef long long ll;
const int P = 998244353;
int n, l, ans; char s[30][30];
bitset<30> chr[30];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = (ll)res * a % P;
a = (ll)a * a % P, b >>= 1;
}
return res;
}
int main() {
cin >> n >> l;
for (int i = 1; i <= n; ++i) {
cin >> s[i]+1;
for (int j = 1; j <= strlen(s[i]+1); ++j) chr[i][s[i][j]-'a'+1] = 1;
}
for (int i = 1; i <= (1<<n)-1; ++i) {
int cnt = 0; bitset<30> now;
for (int j = 0; j < 30; ++j) now[j] = 1;
for (int j = 0; j < n; ++j) if ((i >> j) & 1) cnt ++, now &= chr[j+1];
(ans += (cnt&1) ? power(now.count(), l) : P-power(now.count(), l)) %= P;
}
cout << ans << '\n';
return 0;
}
9.31
ABC 322 不好评价。
ABC322A. First ABC 2:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
int n;
char s[N];
int main() {
cin >> n >> s+1;
for (int i = 3; i <= n; ++i) {
if (s[i-2] == 'A' && s[i-1] == 'B' && s[i] == 'C')
printf("%d\n", i-2), exit(0);
}
puts("-1"), exit(0);
}
ABC322B. Prefix and Suffix:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
char s[N], t[N];
int main() {
cin >> n >> m >> s+1 >> t+1;
bool pre = 1, suf = 1;
for (int i = 1; i <= n; ++i) if (s[i] != t[i]) pre = 0;
for (int i = 1; i <= n; ++i) if (s[n-i+1] != t[m-i+1]) suf = 0;
if (pre && suf) puts("0");
else if (pre) puts("1");
else if (suf) puts("2");
else puts("3");
}
ABC322C. Festival:二分(橙)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int n, m, a[N];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) printf("%d\n", a[lower_bound(a+1, a+m+1, i)-a]-i);
}
ABC322F. Vacation Query:线段树(蓝)
实际上是对一个
- 区间取反;
- 求区间最大连续
的长度。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 5e5+10;
int n, m;
char s[N];
struct Node {
int l, r, tag;
int cnt0 = 0, cnt1 = 0, l0 = 0, l1 = 0, r0 = 0, r1 = 0, m0 = 0, m1 = 0;
} seg[N<<2];
void pushup(Node &U, Node &L, Node &R) {
U.cnt0 = L.cnt0 + R.cnt0, U.cnt1 = L.cnt1 + R.cnt1;
U.l0 = L.cnt1 ? L.l0 : L.cnt0+R.l0, U.l1 = L.cnt0 ? L.l1 : L.cnt1+R.l1;
U.r0 = R.cnt1 ? R.r0 : R.cnt0+L.r0, U.r1 = R.cnt0 ? R.r1 : R.cnt1+L.r1;
U.m0 = max(max(L.m0, R.m0), L.r0+R.l0), U.m1 = max(max(L.m1, R.m1), L.r1+R.l1);
}
void modify(int u) {
seg[u].tag ^= 1;
swap(seg[u].cnt0, seg[u].cnt1), swap(seg[u].l0, seg[u].l1), swap(seg[u].r0, seg[u].r1), swap(seg[u].m0, seg[u].m1);
}
void pushup(int u) {
pushup(seg[u], seg[u<<1], seg[u<<1|1]);
}
void pushdown(int u) {
if (!seg[u].tag) return ;
modify(u<<1), modify(u<<1|1), seg[u].tag = 0;
}
void build(int u, int l, int r) {
seg[u].l = l, seg[u].r = r;
if (l == r) {
if (s[l] == '0') seg[u].cnt0 = seg[u].l0 = seg[u].r0 = seg[u].m0 = 1;
else seg[u].cnt1 = seg[u].l1 = seg[u].r1 = seg[u].m1 = 1;
return ;
}
int mid = l + r >> 1;
build(u<<1, l, mid), build(u<<1|1, mid+1, r);
pushup(u);
}
void modify(int u, int l, int r) {
if (seg[u].l >= l && seg[u].r <= r) {modify(u); return ;}
pushdown(u);
int mid = seg[u].l + seg[u].r >> 1;
if (l <= mid) modify(u<<1, l, r);
if (r > mid) modify(u<<1|1, l, r);
pushup(u);
}
Node query(int u, int l, int r) {
if (seg[u].l >= l && seg[u].r <= r) return seg[u];
pushdown(u);
int mid = seg[u].l + seg[u].r >> 1; Node res, L, R;
if (l <= mid) L = query(u<<1, l, r);
if (r > mid) R = query(u<<1|1, l, r);
pushup(res, L, R);
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> s+1;
build(1, 1, n);
int op, l, r;
while (m -- ) {
cin >> op >> l >> r;
if (op == 1) modify(1, l, r);
else cout << query(1, l, r).m1 << '\n';
}
return 0;
}
本文作者:Jasper08
本文链接:https://www.cnblogs.com/Jasper08/p/17675757.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步