C
我们用 \(1\sim K\) 的和减去出现在 \(1\sim K\) 中的数的和。
int n, k, a[N], res;
map<int, int> vis;
signed main() {
cin >> n >> k;
_for(i, 1, n) cin >> a[i];
res = k * (1 + k) / 2;
_for(i, 1, n) if (a[i] >= 1 && a[i] <= k && !vis[a[i]]) res -= a[i], vis[a[i]] = 1;
cout << res << endl;
}
D
这个题我脑溢血了,最开始从 \(2\) 维 dp 开始写,写了 10 min 发现假了,加了一维又调了好久好久。
\(dp_{i,j,k}\) 表示前 \(i\) 个数,存不存在有两个字符相邻 \(j=0/1\),当前这个位置翻转还是不翻转 \(k=0/1\)。
没有脑血栓真建议别看我代码,我写了 30 分钟。
int n, b[N], dp[N][2][2];
char a[N];
char get(char c, int x) {
if (c == '0') {
if (x == 1) return '1';
return '0';
}
else {
if (x == 1) return '0';
return '1';
}
}
signed main() {
memset(dp, 0x3f, sizeof dp);
cin >> n >> (a + 1);
_for(i, 1, n) cin >> b[i];
dp[1][0][0] = 0;
dp[1][0][1] = b[1];
_for(i, 2, n) {
_for(j, 0, 1) {
_for(k, 0, 1) {
_for(w, 0, j) {
int t = 0;
if (a[i] == get(a[i - 1], k)) {
if (j == w) dp[i][j][1] = min(dp[i][j][1], dp[i - 1][w][k] + b[i]);
if (w == 0 && j == 1) dp[i][j][0] = min(dp[i][j][0], dp[i - 1][w][k]);
}
else {
if (w == 0 && j == 1) dp[i][j][1] = min(dp[i][j][1], dp[i - 1][w][k] + b[i]);
if (j == w) dp[i][j][0] = min(dp[i][j][0], dp[i - 1][w][k]);
}
}
}
}
}
cout << min(dp[n][1][0], dp[n][1][1]) << endl;
}
E
我们把操作倒着看。自己画一下就知道,倒着来看操作的话,每操作一列,其实就代表之后所有的操作都不会影响到这一列,等价于把这一列删了。
int n, m, c;
int t[N], a[N], x[N], vis[3][N], ans[N], res, cc;
signed main() {
cin >> n >> m >> c;
int tn = n, tm = m;
_for(i, 1, c) cin >> t[i] >> a[i] >> x[i];
_pfor(i, c, 1) {
if (vis[t[i]][a[i]]) continue;
vis[t[i]][a[i]] = 1;
if (t[i] == 1) ans[x[i]] += m, n--; // n--代表删除这一行
else ans[x[i]] += n, m--;
}
_for(i, 1, N - 5) res += ans[i], cc += (ans[i] > 0);
if (res == tn * tm) cout << cc << endl;
else {
cout << cc + 1 << endl;
cout << 0 << ' ' << tn * tm - res << endl;
}
_for(i, 1, N - 5) {
if (ans[i]) cout << i << ' ' << ans[i] << endl;
}
}
F
肯定考虑二分 \(x\)。这就意味着对于 \(T\) 的所有字符,都要去对应 \(f(S,N)\) 中的对应字符 \(x\) 次。我们不用这样考虑:如果当前 S 的指针匹配失败了,就把指针跳到开头再来匹配。我们直接把 \(S\) 复制 \(N\) 次,看成 \(len(S)\times N\) 长度的字符串就行。可以避免大量分讨。
还不明白?看代码吧。这样复杂度是 \(O(n\times \log^2(len(S)\times N))\)。非常卡常数。
有一个卡常技巧,把 sum 数组的小的那一维放前面去,直接快 \(0.5s\sim 1.5s\) 不等!我十分震撼!
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
//#define int long long
const int N = 1e5 + 5;
LL T;
int n, m, sum[27][N];
// sum_{c,i} 表示S中前 $i$ 个字符c出现了多少次
char a[N], b[N];
inline LL get(LL x, int c) {
return 1ll * sum[c][n] * (x / n) + sum[c][x % n]; // 代表复制后的S中前x个字符c出现了对少次
}
inline bool check(LL x) {
if (x == 0) return 1;
LL lst = 1;
_for(i, 1, m) {
int t = (int)(b[i] - 'a');
LL l = lst, r = T * n;
if (l > r) return 0;
while (l < r) {
LL mid = (l + r) >> 1;
if (get(mid, t) - get(lst - 1, t) >= x) r = mid;
else l = mid + 1;
}
if (get(l, t) - get(lst - 1, t) < x) return 0;
lst = l + 1;
}
return 1;
}
signed main() {
scanf("%lld%s%s", &T, (a + 1), (b + 1));
n = strlen(a + 1), m = strlen(b + 1);
_for(i, 1, n) {
int t = (int)(a[i] - 'a');
_for(j, 0, 25) {
if (t == j) sum[j][i] = sum[j][i - 1] + 1;
else sum[j][i] = sum[j][i - 1];
}
}
LL l = 0, r = T * n;
while (l < r) {
LL mid = (l + r + 1) >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
if (!check(l)) puts("0");
else cout << l << endl;
}
G
赛时写主席树+树状数组真的是脑抽死了,关键是还是假算法。
我们考虑每一个数能在哪些区间中只出现一次。当前数字下标为 \(i\),往前看第一个和这个数字相同的下标为 \(l_i\),往后看第一个和这个数字相同的下标为 \(r_i\),那么答案就是 \((i-l_i)\times (r_i-i)\)。
但是会算重。所以我们看成 \(n\) 个左上角为 \((l_i,i)\) ,右下角 \((i,r_i)\) 的矩形,求这些矩形的面积并。
#include <bits/stdc++.h>
using namespace std;
#define ls p << 1
#define rs p << 1 | 1
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long
const int N = 4e5 + 5;
int n, a[N], l[N], r[N], vis[N], b[N], n2, res;
struct edge {
int x, l, r, val;
}ed[N];
bool cmp(edge x, edge y) {
return x.x < y.x;
}
int tot;
struct tt {
int l, r, cnt, len;
}tree[N * 4];
void push_up(int p) {
if (tree[p].cnt) tree[p].len = b[tree[p].r + 1] - b[tree[p].l];
else tree[p].len = tree[ls].len + tree[rs].len;
}
void build(int p, int l, int r) {
tree[p].l = l, tree[p].r = r;
if (l == r) return;
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
}
void modify(int p, int l, int r, int k) {
if (l <= tree[p].l && tree[p].r <= r) {
tree[p].cnt += k;
push_up(p);
return;
}
int mid = (tree[p].l + tree[p].r) >> 1;
if (l <= mid) modify(ls, l, r, k);
if (r > mid) modify(rs, l, r, k);
push_up(p);
}
int get_rk(int x) {
return lower_bound(b + 1, b + n2 + 1, x) - b;
}
signed main() {
cin >> n;
_for(i, 1, n) cin >> a[i];
_for(i, 1, n) l[i] = vis[a[i]], vis[a[i]] = i;
fill(vis, vis + N - 5, n + 1);
_pfor(i, n, 1) r[i] = vis[a[i]], vis[a[i]] = i;
_for(i, 1, n) b[++n2] = r[i], b[++n2] = i;
sort(b + 1, b + n2 + 1);
n2 = unique(b + 1, b + n2 + 1) - b - 1;
_for(i, 1, n) r[i] = lower_bound(b + 1, b + n2 + 1, r[i]) - b;
_for(i, 1, n) {
ed[++tot] = {l[i], get_rk(i), r[i], 1};
ed[++tot] = {i, get_rk(i), r[i], -1};
}
sort(ed + 1, ed + tot + 1, cmp);
build(1, 1, n2);
_for(i, 1, tot) {
modify(1, ed[i].l, ed[i].r - 1, ed[i].val);
if (i < tot) res += (ed[i + 1].x - ed[i].x) * tree[1].len;
}
cout << res << endl;
}
标签:AtCoder,return,Beginner,Contest,int,tree,mid,vis,dp
From: https://www.cnblogs.com/stOtue/p/18095419