Mobile wallpaper 1Mobile wallpaper 2Mobile wallpaper 3Mobile wallpaper 4Mobile wallpaper 5
2330 字
12 分钟
CSES Adcanced Techniques
2026-01-19
统计加载中...

更新 ing(但是会优先补 vp ICPC 没做出来的题)

Meet in the Middle#

对半分,两半分别暴力所有情况。最后排序,遍历一边的,用二分或者双指针维护另一边的合法的组合的范围。

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 40;
int a[N], lb, lc;
LL b[1 << 20], c[1 << 20];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, x;
cin >> n >> x;
for (int i = 0; i < n; ++i) cin >> a[i];
int mid = n >> 1;
for (int i = 0; i < (1 << mid); ++i) {
LL s = 0;
for (int j = 0; j < mid; ++j) {
if (i >> j & 1) s += a[j];
}
b[lb++] = s;
}
for (int i = 0; i < (1 << n - mid); ++i) {
LL s = 0;
for (int j = 0; j < n - mid; ++j) {
if (i >> j & 1) s += a[j + mid];
}
c[lc++] = s;
}
sort(b, b + lb), sort(c, c + lc);
LL res = 0;
for (int i = 0; i < lb; ++i) {
res += upper_bound(c, c + lc, x - b[i]) - lower_bound(c, c + lc, x - b[i]);
}
cout << res << endl;
return 0;
}

Hamming Distance#

直接暴力就行了。

#include <iostream>
#include <bitset>
using namespace std;
const int N = 20010;
bitset<31> a[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, k;
cin >> n >> k;
int res = 40;
for (int i = 1; i <= n; ++i) {
string s;
int t = 0;
cin >> s;
for (char c : s) t = (t << 1) + (c == '1');
a[i] = t;
}
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
res = min(res, (int)(a[i] ^ a[j]).count());
}
}
cout << res << endl;
return 0;
}

Corner Subgrid Check#

卡不过去😭

Corner Subgrid Count#

思路应该很显然,开 bitset 存位置,暴力枚举所有行号组合两个二进制串与一下,设 1 的数量为 c,对答案贡献 (c2)\binom{c}{2}.然后就是怎么卡常都过不去,最后看别人开了个 Ofast 优化过去了,然后我也开,993ms 极限跑过去了。

#pragma GCC optimize("Ofast")
#include <iostream>
#include <bitset>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 3010;
char a[N][N];
bitset<N> b[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> (a[i] + 1);
for (int j = 1; j <= n; ++j) {
b[i][j] = a[i][j] == '1';
}
}
LL res = 0;
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
LL c = (b[i] & b[j]).count();
res += c * (c - 1) / 2;
}
}
cout << res << endl;
return 0;
}

Reachable Nodes#

用 bitset 记录能到的点,用记搜做反拓扑序 dp。

#include <iostream>
#include <bitset>
using namespace std;
const int N = 50010, M = 100010;
bitset<N> f[N];
bool v[N];
int ver[M], ne[M], head[N], tot;
void add(int x, int y) {
ver[++tot] = y;
ne[tot] = head[x];
head[x] = tot;
}
void dfs(int x) {
if (v[x]) return;
v[x] = true;
f[x][x] = true;
for (int i = head[x]; i; i = ne[i]) {
int y = ver[i];
dfs(y);
f[x] |= f[y];
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
add(x, y);
}
for (int i = 1; i <= n; ++i) {
if (!v[i]) dfs(i);
cout << f[i].count() << ' ';
}
cout << endl;
return 0;
}

Reachability Queries#

上一道的增强版。强联通分量缩点之后在反拓扑序 DP 即可。

#include <iostream>
#include <bitset>
using namespace std;
const int N = 50010, M = 200010;
int h1[N], h2[N], ver[M], ne[M], tot;
int st[N], tp;
int dfn[N], low[N], scc_id[N], scc_cnt, t;
bool ins[N];
bool vis[N];
bitset<N> f[N];
void add(int *head, int x, int y) {
ver[++tot] = y;
ne[tot] = head[x];
head[x] = tot;
}
void tarjan(int x) {
dfn[x] = low[x] = ++t;
st[++tp] = x;
ins[x] = true;
for (int i = h1[x]; i; i = ne[i]) {
int y = ver[i];
if(!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
}
else if (ins[y]) low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x]) {
int y;
scc_cnt++;
do {
y = st[tp--];
ins[y] = false;
scc_id[y] = scc_cnt;
} while (y != x);
}
}
void dfs(int x) {
if (vis[x]) return;
vis[x] = true;
f[x][x] = true;
for (int i = h2[x]; i; i = ne[i]) {
int y = ver[i];
dfs(y);
f[x] |= f[y];
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
add(h1, x, y);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) tarjan(i);
}
for (int i = 1; i <= n; ++i) {
for (int j = h1[i]; j; j = ne[j]) {
int y = ver[j];
if (scc_id[i] != scc_id[y]) {
add(h2, scc_id[i], scc_id[y]);
}
}
}
while (q--) {
int a, b;
cin >> a >> b;
a = scc_id[a], b = scc_id[b];
dfs(a);
cout << (f[a][b] ? "YES" : "NO") << endl;
}
return 0;
}

Cut and Paste#

可以用 FHQ-Treap(现学的)。这是一种基于分裂和合并的 Treap,感觉写起来反而还比一般的 Treap 好写。

#include <iostream>
#include <random>
#include <ctime>
using namespace std;
const int N = 200010;
mt19937 rnd(time(nullptr));
struct Node {
int ls, rs;
int cnt;
char c;
unsigned pri;
} tr[N];
int tot, rt;
void pushup(int u) {
tr[u].cnt = tr[tr[u].ls].cnt + tr[tr[u].rs].cnt + 1;
}
void split(int u, int k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
int lsiz = tr[tr[u].ls].cnt + 1;
if (lsiz <= k) x = u, split(tr[u].rs, k - lsiz, tr[u].rs, y);
else y = u, split(tr[u].ls, k, x, tr[u].ls);
pushup(u);
}
int merge(int u, int v) {
if (!u || !v) return u | v;
if (tr[u].pri < tr[v].pri) {
tr[u].rs = merge(tr[u].rs, v);
pushup(u);
return u;
}
else {
tr[v].ls = merge(u, tr[v].ls);
pushup(v);
return v;
}
}
int newNode(char c) {
tr[++tot] = {0, 0, 1, c, rnd()};
return tot;
}
void print(int u) {
if (!u) return;
else {
print(tr[u].ls);
cout << tr[u].c;
print(tr[u].rs);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, q;
string s;
cin >> n >> q >> s;
for (char c : s) rt = merge(rt, newNode(c));
while (q--) {
int l, r;
cin >> l >> r;
int lt, mt, rrt;
split(rt, l - 1, lt, mt);
split(mt, r - l + 1, mt, rrt);
rt = merge(lt, rrt);
rt = merge(rt, mt);
}
print(rt);
cout << endl;
return 0;
}

Substring Reversals#

仍然可以用 FHQ-Treap,这次的区间反转需要打懒标记。

#include <iostream>
#include <random>
#include <ctime>
using namespace std;
const int N = 200010;
mt19937 rnd(time(nullptr));
struct Node {
int ls, rs;
int cnt;
char c;
bool tag;
unsigned pri;
} tr[N];
int tot, rt;
void pushup(int u) {
tr[u].cnt = tr[tr[u].ls].cnt + tr[tr[u].rs].cnt + 1;
}
void pushdown(int u) {
if (tr[u].tag) {
if (tr[u].ls) swap(tr[tr[u].ls].ls, tr[tr[u].ls].rs), tr[tr[u].ls].tag ^= 1;
if (tr[u].rs) swap(tr[tr[u].rs].ls, tr[tr[u].rs].rs), tr[tr[u].rs].tag ^= 1;
tr[u].tag = 0;
}
}
void split(int u, int k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
pushdown(u);
int lsiz = tr[tr[u].ls].cnt + 1;
if (lsiz <= k) x = u, split(tr[u].rs, k - lsiz, tr[u].rs, y);
else y = u, split(tr[u].ls, k, x, tr[u].ls);
pushup(u);
}
int merge(int u, int v) {
if (!u || !v) return u | v;
if (tr[u].pri < tr[v].pri) {
pushdown(u);
tr[u].rs = merge(tr[u].rs, v);
pushup(u);
return u;
}
else {
pushdown(v);
tr[v].ls = merge(u, tr[v].ls);
pushup(v);
return v;
}
}
int newNode(char c) {
tr[++tot] = {0, 0, 1, c, 0, rnd()};
return tot;
}
void print(int u) {
if (!u) return;
else {
pushdown(u);
print(tr[u].ls);
cout << tr[u].c;
print(tr[u].rs);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, q;
string s;
cin >> n >> q >> s;
for (char c : s) rt = merge(rt, newNode(c));
while (q--) {
int l, r;
cin >> l >> r;
int lt, mt, rrt;
split(rt, l - 1, lt, mt);
split(mt, r - l + 1, mt, rrt);
swap(tr[mt].ls, tr[mt].rs);
tr[mt].tag ^= 1;
rt = merge(lt, mt);
rt = merge(rt, rrt);
}
print(rt);
cout << endl;
return 0;
}

Reversals and Sums#

同上,多维护一个 sum,pushup 的时候更新即可。

#include <iostream>
#include <random>
#include <ctime>
using namespace std;
typedef long long LL;
const int N = 200010;
mt19937 rnd(time(nullptr));
struct Node {
int ls, rs;
int cnt;
LL val, s;
bool tag;
unsigned pri;
} tr[N];
int tot, rt;
void pushup(int u) {
tr[u].cnt = tr[tr[u].ls].cnt + tr[tr[u].rs].cnt + 1;
tr[u].s = tr[tr[u].ls].s + tr[tr[u].rs].s + tr[u].val;
}
void pushdown(int u) {
if (tr[u].tag) {
if (tr[u].ls) swap(tr[tr[u].ls].ls, tr[tr[u].ls].rs), tr[tr[u].ls].tag ^= 1;
if (tr[u].rs) swap(tr[tr[u].rs].ls, tr[tr[u].rs].rs), tr[tr[u].rs].tag ^= 1;
tr[u].tag = 0;
}
}
void split(int u, int k, int &x, int &y) {
if (!u) {
x = y = 0;
return;
}
pushdown(u);
int lsiz = tr[tr[u].ls].cnt + 1;
if (lsiz <= k) x = u, split(tr[u].rs, k - lsiz, tr[u].rs, y);
else y = u, split(tr[u].ls, k, x, tr[u].ls);
pushup(u);
}
int merge(int u, int v) {
if (!u || !v) return u | v;
if (tr[u].pri < tr[v].pri) {
pushdown(u);
tr[u].rs = merge(tr[u].rs, v);
pushup(u);
return u;
}
else {
pushdown(v);
tr[v].ls = merge(u, tr[v].ls);
pushup(v);
return v;
}
}
int newNode(int t) {
tr[++tot] = {0, 0, 1, t, t, 0, rnd()};
return tot;
}
void print(int u) {
if (!u) return;
else {
pushdown(u);
print(tr[u].ls);
cout << tr[u].val << ' ';
print(tr[u].rs);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, q;
cin >> n >> q;
for (int i = 1; i <= n; ++i) {
int t;
cin >> t;
rt = merge(rt, newNode(t));
}
while (q--) {
int op, a, b;
cin >> op >> a >> b;
int l, m, r;
split(rt, a - 1, l, m);
split(m, b - a + 1, m, r);
if (op == 1) {
swap(tr[m].ls, tr[m].rs);
tr[m].tag ^= 1;
}
else cout << tr[m].s << endl;
rt = merge(merge(l, m), r);
}
return 0;
}

Necessary Roads#

跑无向图 tarjan 找到桥即可。

#include <iostream>
using namespace std;
const int N = 100010, M = 200010;
int head[N], ver[M * 2], ne[M * 2], tot = 1;
int dfn[N], low[N], t;
bool bridge[M * 2];
int cnt;
void add(int x, int y) {
ver[++tot] = y;
ne[tot] = head[x];
head[x] = tot;
}
void tarjan(int x, int from) {
dfn[x] = low[x] = ++t;
for (int i = head[x]; i; i = ne[i]) {
if (i == (from ^ 1)) continue;
int y = ver[i];
if (!dfn[y]) {
tarjan(y, i);
low[x] = min(low[x], low[y]);
if (dfn[x] < low[y]) bridge[i] = bridge[i ^ 1] = true, cnt++;
}
else low[x] = min(low[x], dfn[y]);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
add(x, y), add(y, x);
}
tarjan(1, 0);
cout << cnt << endl;
for (int i = 2; i <= tot; i += 2) {
if (bridge[i]) cout << ver[i ^ 1] << ' ' << ver[i] << endl;
}
return 0;
}

Necessary Cities#

跑无向图 tarjan 找到割点即可。

#include <iostream>
using namespace std;
const int N = 100010, M = 200010;
int head[N], ver[M * 2], ne[M * 2], tot = 1;
int dfn[N], low[N], t;
bool cut[N];
void add(int x, int y) {
ver[++tot] = y;
ne[tot] = head[x];
head[x] = tot;
}
void tarjan(int x, int from) {
dfn[x] = low[x] = ++t;
int f = 0;
for (int i = head[x]; i; i = ne[i]) {
if (i == (from ^ 1)) continue;
int y = ver[i];
if (!dfn[y]) {
tarjan(y, i);
low[x] = min(low[x], low[y]);
if (x == 1) {
if (++f == 2) cut[x] = true;
}
else if (dfn[x] <= low[y]) cut[x] = true;
}
else low[x] = min(low[x], dfn[y]);
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int x, y;
cin >> x >> y;
add(x, y), add(y, x);
}
tarjan(1, 0);
int cnt = 0;
for (int i = 1; i <= n; ++i) if (cut[i]) cnt++;
cout << cnt << endl;
for (int i = 1; i <= n; ++i) if (cut[i]) cout << i << ' ';
cout << endl;
return 0;
}
CSES Adcanced Techniques
https://starlab.top/posts/cses-adcanced-techniques/
作者
Star
发布于
2026-01-19
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时