练习选讲(2023.10)
10 月
10.1
P2572 [SCOI2010] 序列操作:线段树(紫)
维护每个区间的两个懒标记,
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, m, a[N];
struct Node {
int l, r, tag1=-1, tag2=0;
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.l0+R.l0, U.l1 = L.cnt0?L.l1:L.l1+R.l1;
U.r0 = R.cnt1?R.r0:R.r0+L.r0, U.r1 = R.cnt0?R.r1:R.r1+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 pushup(int u) {
pushup(seg[u], seg[u<<1], seg[u<<1|1]);
}
void modify(Node &U, int op) {
if (op == 0) U.tag1=U.tag2=0, U.cnt0=U.l0=U.r0=U.m0=U.r-U.l+1, U.cnt1=U.l1=U.r1=U.m1=0;
else if (op == 1) U.tag1=1, U.tag2=0, U.cnt0=U.l0=U.r0=U.m0=0, U.cnt1=U.l1=U.r1=U.m1=U.r-U.l+1;
else U.tag2 ^= 1, swap(U.cnt0, U.cnt1), swap(U.l0, U.l1), swap(U.r0, U.r1), swap(U.m0, U.m1);
}
void pushdown(Node &U, Node &L, Node &R) {
if (U.tag1 != -1) modify(L, U.tag1), modify(R, U.tag1), U.tag1 = -1;
if (U.tag2) modify(L, 2), modify(R, 2), U.tag2 = 0;
}
void pushdown(int u) {
pushdown(seg[u], seg[u<<1], seg[u<<1|1]);
}
void build(int u, int l, int r) {
seg[u].l = l, seg[u].r = r;
if (l == r) {
if (a[l]) seg[u].cnt1 = seg[u].l1 = seg[u].r1 = seg[u].m1 = 1;
else seg[u].cnt0 = seg[u].l0 = seg[u].r0 = seg[u].m0 = 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, int op) {
if (seg[u].l >= l && seg[u].r <= r) {modify(seg[u], op); return ;}
pushdown(u);
int mid = seg[u].l + seg[u].r >> 1;
if (l <= mid) modify(u<<1, l, r, op);
if (r > mid) modify(u<<1|1, l, r, op);
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() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
build(1, 1, n);
int op, l, r;
while (m -- ) {
scanf("%d%d%d", &op, &l, &r); l ++, r ++;
if (op == 0) modify(1, l, r, 0);
else if (op == 1) modify(1, l, r, 1);
else if (op == 2) modify(1, l, r, 2);
else if (op == 3) printf("%d\n", query(1, l, r).cnt1);
else printf("%d\n", query(1, l, r).m1);
}
return 0;
}
10.2
ABC249F. Ignore Operations:堆,贪心(绿)
可以发现,进行一次 1 操作后,前面的所有操作都相当于作废了。我们考虑倒着做,维护当前删除 2 操作后得到的结果最大值
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n, k, op[N], a[N]; ll ans = -9e18, sum;
priority_queue<int> q;
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d%d", &op[i], &a[i]);
for (int i = n; i >= 1 && k >= 0; --i) {
if (op[i] == 1) ans = max(ans, sum+a[i]), k --;
else {
if (a[i] >= 0) sum += a[i];
else q.push(a[i]);
}
while ((int)q.size() > k) sum += q.top(), q.pop();
}
ans = max(ans, sum);
printf("%lld\n", ans);
return 0;
}
SP13015. CNTPRIME - Counting Primes:珂朵莉树,质数筛(黄)
两个板子。
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int N = 1e6+10;
int T, n, m, a[N];
int idx, prime[N]; bool st[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;
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 v) {
iter itr = split(r+1), itl = split(l);
s.erase(itl, itr), s.insert(Node(l, r, v));
}
int query(int l, int r) {
int cnt = 0; iter itr = split(r+1), itl = split(l);
for (iter it = itl; it != itr; ++it) if (!st[it->v]) cnt += it->r-it->l+1;
return cnt;
}
void get_prime() {
st[0] = st[1] = 1;
for (int i = 2; i <= 1000000; ++i) {
if (!st[i]) prime[++ idx] = i;
for (int j = 1; j <= idx && prime[j] <= 1000000/i; ++j) {
st[i*prime[j]] = 1;
if (i % prime[j] == 0) break;
}
}
}
void solve() {
scanf("%d%d", &n, &m); s.clear();
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), s.insert(Node(i, i, a[i]));
int op, l, r, v;
while (m -- ) {
scanf("%d%d%d", &op, &l, &r);
if (op == 0) scanf("%d", &v), assign(l, r, v);
else printf("%d\n", query(l, r));
}
}
int main() {
scanf("%d", &T); get_prime();
for (int i = 1; i <= T; ++i) printf("Case %d:\n", i), solve();
return 0;
}
P1558 色板游戏:线段树(绿)
注意到颜色数很小,可以状压表示。时间复杂度
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5+10;
int n, t, m;
struct Node {
int l, r, s = 0, tag = 0;
} seg[N<<2];
void pushup(Node &U, Node &L, Node &R) {
U.s = L.s | R.s;
}
void pushup(int u) {
pushup(seg[u], seg[u<<1], seg[u<<1|1]);
}
void modify(Node &U, int c) {
U.s = U.tag = c;
}
void pushdown(Node &U, Node &L, Node &R) {
if (!U.tag) return ;
modify(L, U.tag), modify(R, U.tag), U.tag = 0;
}
void pushdown(int u) {
pushdown(seg[u], seg[u<<1], seg[u<<1|1]);
}
void build(int u, int l, int r) {
seg[u].l = l, seg[u].r = r;
if (l == r) {seg[u].s = 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, int c) {
if (seg[u].l >= l && seg[u].r <= r) {modify(seg[u], c); return ;}
pushdown(u);
int mid = seg[u].l + seg[u].r >> 1;
if (l <= mid) modify(u<<1, l, r, c);
if (r > mid) modify(u<<1|1, l, r, c);
pushup(u);
}
int query(int u, int l, int r) {
if (seg[u].l >= l && seg[u].r <= r) return seg[u].s;
pushdown(u);
int mid = seg[u].l + seg[u].r >> 1; int res = 0;
if (l <= mid) res |= query(u<<1, l, r);
if (r > mid) res |= query(u<<1|1, l, r);
return res;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> t >> m;
build(1, 1, n);
char op; int a, b, c;
while (m -- ) {
cin >> op >> a >> b; if (a > b) swap(a, b);
if (op == 'C') cin >> c, modify(1, a, b, 1<<c-1);
else {
int s = query(1, a, b), cnt = 0;
for (int i = 0; i < t; ++i) if ((s >> i) & 1) cnt ++;
cout << cnt << '\n';
}
}
return 0;
}
P3740 [HAOI2014] 贴海报:线段树,珂朵莉树(绿)
珂朵莉树裸题。
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int N = 1010;
int n, m, idx; bool nums[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;
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 v) {
iter itr = split(r+1), itl = split(l);
s.erase(itl, itr), s.insert(Node(l, r, v));
}
int count(int l, int r) {
int cnt = 0; iter itr = split(r+1), itl = split(l); nums[0] = 1;
for (iter it = itl; it != itr; ++it) if (!nums[it->v]) nums[it->v] = 1, cnt ++;
return cnt;
}
int main() {
scanf("%d%d", &n, &m);
s.insert(Node(1, n, 0));
while (m -- ) {
int l, r; scanf("%d%d", &l, &r);
assign(l, r, ++ idx);
}
printf("%d\n", count(1, n));
return 0;
}
P9117 [春季测试 2023] 涂色游戏:模拟(橙)
对于每一行每一列,记录其最后一次修改的时间和颜色。对于点
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5+10;
int T, n, m, q;
pii row[N], col[N];
void solve() {
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) row[i] = {0, 0};
for (int i = 1; i <= m; ++i) col[i] = {0, 0};
int op, x, c;
for (int i = 1; i <= q; ++i) {
scanf("%d%d%d", &op, &x, &c);
if (!op) row[x] = {i, c};
else col[x] = {i, c};
}
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
printf("%d ", max(row[i], col[j]).second);
puts("");
}
}
int main() {
scanf("%d", &T);
while (T --) solve();
return 0;
}
P8512 [Ynoi Easy Round 2021] TEST_152:珂朵莉树,树状数组(紫)
区间覆盖?珂朵莉树!
由于一次操作至多增加
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
const int N = 5e5+10;
int n, m, q; ll c[N], ans[N];
int lowbit(int x) {
return x & -x;
}
void add(int x, ll k) {
for (int i = x; i <= n; i += lowbit(i)) c[i] += k;
}
ll query(int x) {
ll res = 0;
for (int i = x; i >= 1; i -= lowbit(i)) res += c[i];
return res;
}
struct Operation {
int l, r, v;
} oper[N];
struct Query {
int l, r, id;
bool operator < (const Query &T) const {
return (r == T.r) ? l < T.l : r < T.r;
}
} qry[N];
struct Node {
int l, r, t; mutable int v;
Node (int l, int r = 0, int v = 0, int t = 0) : l(l), r(r), v(v), t(t) {}
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 --;
int l = it->l, r = it->r, v = it->v, t = it->t;
s.erase(it), s.insert(Node(l, pos-1, v, t));
return s.insert(Node(pos, r, v, t)).first;
}
void assign(int l, int r, int v, int t) {
auto itr = split(r+1), itl = split(l);
for (auto it = itl; it != itr; ++it) if (it->t) add(it->t, -1ll*(it->r-it->l+1)*it->v);
s.erase(itl, itr), s.insert(Node(l, r, v, t)), add(t, 1ll*(r-l+1)*v);
}
int main() {
scanf("%d%d%d", &n, &m, &q); s.insert(Node(1, n, 0, 0));
int l, r, v, id;
for (int i = 1; i <= n; ++i) scanf("%d%d%d", &l, &r, &v), oper[i] = {l, r, v};
for (int i = 1; i <= q; ++i) scanf("%d%d", &l, &r), qry[i] = {l, r, i};
sort(qry+1, qry+q+1);
int now = 0;
for (int i = 1; i <= q; ++i) {
l = qry[i].l, r = qry[i].r, id = qry[i].id;
while (now < r) now ++, assign(oper[now].l, oper[now].r, oper[now].v, now);
ans[id] = query(r) - query(l-1);
}
for (int i = 1; i <= q; ++i) printf("%lld\n", ans[i]);
return 0;
}
P5104 红包发红包:数学,期望(绿)
容易猜到剩下
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define int long long
const int P = 1e9+7;
int w, n, k;
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) (res *= a) %= P;
(a *= a) %= P, b >>= 1;
}
return res;
}
signed main() {
cin >> w >> n >> k;
cout << w * power(power(2, k), P-2)%P << '\n';
return 0;
}
CF472D. Design Tutorial: Inverse the Problem:最小生成树,dfs(蓝)
将这个邻接矩阵看作一张图,跑一遍最小生成树,用 dfs 判断即可。注意一些细枝末节的判断。
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
typedef pair<int, int> pii;
const int N = 2010;
struct Edge {
int u, v, w;
bool operator < (const Edge &T) const {
return w < T.w;
}
}; vector<Edge> e;
int n, a[N][N], p[N], dist[N][N];
vector<pii> E[N];
int find(int x) {
return p[x] == x ? p[x] : p[x] = find(p[x]);
}
void dfs(int u, int now) {
for (auto edge : E[now]) {
int v = edge.first, w = edge.second;
if (!dist[u][v] && u != v) dist[u][v] = dist[u][now]+w, dfs(u, v);
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) scanf("%d", &a[i][j]);
for (int i = 1; i <= n; ++i) {
p[i] = i;
for (int j = 1; j <= i; ++j) {
if (i == j) {if (a[i][j]) puts("NO"), exit(0);}
else if (a[i][j] != a[j][i] || a[i][j] < 1) puts("NO"), exit(0);
e.push_back({i, j, a[i][j]});
}
}
sort(e.begin(), e.end());
int now = n-1;
for (auto edge : e) {
int u = edge.u, v = edge.v, w = edge.w;
int fu = find(u), fv = find(v);
if (fu == fv) continue;
p[fu] = fv, now --, E[u].push_back({v, w}), E[v].push_back({u, w});
if (!now) break;
}
for (int i = 1; i <= n; ++i) dfs(i, i);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
if (a[i][j] != dist[i][j])
puts("NO"), exit(0);
}
puts("YES");
return 0;
}
ABC246G. Game on Tree 3:二分,dp(蓝)
显然答案具有单调性。
不妨设当前二分的答案为
所以我们有:
时间复杂度
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 2e5+10;
int n, a[N], f[N];
vector<int> nums, e[N];
void dfs(int u, int fa, int x) {
int sum = 0;
for (auto v : e[u]) {
if (v == fa) continue;
dfs(v, u, x), sum += f[v];
}
f[u] = max(sum-1, 0)+(a[u] >= x);
}
int main() {
scanf("%d", &n); nums.push_back(0);
for (int i = 2; i <= n; ++i) scanf("%d", &a[i]), nums.push_back(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);
}
sort(nums.begin(), nums.end());
int l = 0, r = n-1;
while (l < r) {
int mid = l + r + 1 >> 1; dfs(1, 1, nums[mid]);
if (f[1] >= 1) l = mid;
else r = mid-1;
}
printf("%d\n", nums[l]);
return 0;
}
CF875D. High Cry:单调栈,st 表,二分(紫)
用单调栈求出
点击展开代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n, a[N], L[N], R[N], st[N][25]; ll ans;
stack<int> s;
int query(int l, int r) {
int k = log2(r-l+1);
return st[l][k] | st[r-(1<<k)+1][k];
}
int findL(int l, int r, int x) {
while (l < r) {
int mid = l + r >> 1;
if (query(mid, x) > a[x]) l = mid+1;
else r = mid;
}
return l;
}
int findR(int l, int r, int x) {
while (l < r) {
int mid = l + r + 1 >> 1;
if (query(x, mid) > a[x]) r = mid-1;
else l = mid;
}
return l;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), st[i][0] = a[i];
for (int j = 1; (1<<j) <= n; ++j) {
for (int i = 1; i+(1<<j)-1 <= n; ++i)
st[i][j] = st[i][j-1] | st[i+(1<<j-1)][j-1];
}
for (int i = 1; i <= n; ++i) {
while (s.size() && a[s.top()] <= a[i]) s.pop();
if (s.size()) L[i] = s.top()+1; else L[i] = 1;
s.push(i);
}
while (s.size()) s.pop();
for (int i = n; i >= 1; --i) {
while (s.size() && a[s.top()] < a[i]) s.pop();
if (s.size()) R[i] = s.top()-1; else R[i] = n;
s.push(i);
}
for (int i = 1; i <= n; ++i) {
int l = findL(L[i], i, i), r = findR(i, R[i], i);
ans += ((ll)i-l+1)*(r-i+1);
}
printf("%lld\n", 1ll*n*(n+1)/2-ans);
return 0;
}
10.5
CF103687L. Candy Machine:贪心(绿)
假设最终选择的集合的平均数不超过
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int n, ans, a[N]; ll tot; double aver;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), tot += a[i];
aver = tot / (double)n;
sort(a+1, a+n+1);
int pos = upper_bound(a+1, a+n+1, aver) - a; ans = n-pos+1;
for (int i = n; i > 1; --i) {
tot -= a[i], aver = tot / (double)(i-1);
pos = upper_bound(a+1, a+i, aver) - a, ans = max(ans, i-pos);
}
printf("%d\n", ans);
return 0;
}
P4717 【模板】快速莫比乌斯/沃尔什变换 (FMT/FWT):FWT(紫)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = (1<<17)+10, P = 998244353;
int n, m, a[N], b[N], A[N], B[N];
void init() {
for (int i = 0; i < n; ++i) A[i] = a[i], B[i] = b[i];
}
void FWT_OR(int f[], int op) {
for (int x = 1; x < n; x <<= 1) {
for (int i = 0; i < n; i += x<<1)
for (int j = 0; j < x; ++j)
(f[i+j+x] += f[i+j] * op) %= P;
}
}
void FWT_AND(int f[], int op) {
for (int x = 1; x < n; x <<= 1) {
for (int i = 0; i < n; i += x<<1)
for (int j = 0; j < x; ++j)
(f[i+j] += f[i+j+x] * op) %= P;
}
}
void FWT_XOR(int f[], int op) {
for (int x = 1; x < n; x <<= 1) {
for (int i = 0; i < n; i += x<<1)
for (int j = 0, f1, f2; j < x; ++j)
f1 = f[i+j], f2 = f[i+j+x], f[i+j] = 1ll * (f1+f2) * op % P, f[i+j+x] = 1ll * (f1-f2) * op % P;
}
}
void FWT_CALC() {
for (int i = 0; i < n; ++i) A[i] = 1ll * A[i] * B[i] % P;
}
void output() {
for (int i = 0; i < n; ++i) printf("%d ", (A[i]+P)%P); puts("");
}
int main() {
scanf("%d", &m); n = 1 << m;
for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
for (int i = 0; i < n; ++i) scanf("%d", &b[i]);
init(), FWT_OR(A, 1), FWT_OR(B, 1), FWT_CALC(), FWT_OR(A, -1), output();
init(), FWT_AND(A, 1), FWT_AND(B, 1), FWT_CALC(), FWT_AND(A, -1), output();
init(), FWT_XOR(A, 1), FWT_XOR(B, 1), FWT_CALC(), FWT_XOR(A, 499122177), output();
return 0;
}
10.9
ABC245D. Polynomial division:数学(黄)
其实就是高精度除法模拟。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, m, A[210], B[210], C[210];
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; ++i) scanf("%d", &A[i]);
for (int i = 0; i <= n+m; ++i) scanf("%d", &C[i]);
for (int i = m, j = n+m; i >= 0; --i, --j) {
B[i] = C[j] / A[n];
for (int k = j, t = n; k >= i; --k, --t) C[k] -= B[i] * A[t];
}
for (int i = 0; i <= m; ++i) printf("%d ", B[i]);
return 0;
}
ABC245E. Wrapping Chocolate:排序,贪心(绿)
将所有巧克力和盒子放在一起,分别以 multiset
,否则在 multiset
里找到最小的一个
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
const int N = 4e5+10;
int n, m;
multiset<int> pos;
struct Node {
int x, y, id;
bool operator < (const Node &T) const {
return (x == T.x) ? ((y == T.y) ? id > T.id : y > T.y) : x > T.x;
}
} s[N];
int main() {
scanf("%d%d", &n, &m);
int x, y;
for (int i = 1; i <= n; ++i) scanf("%d", &x), s[i].x = x;
for (int i = 1; i <= n; ++i) scanf("%d", &y), s[i].y = y, s[i].id = 1;
for (int i = 1; i <= m; ++i) scanf("%d", &x), s[n+i].x = x;
for (int i = 1; i <= m; ++i) scanf("%d", &y), s[n+i].y = y, s[n+i].id = 2;
sort(s+1, s+n+m+1);
for (int i = 1; i <= n+m; ++i) {
if (s[i].id == 2) pos.insert(s[i].y);
else {
auto now = pos.lower_bound(s[i].y);
if (now == pos.end()) puts("No"), exit(0);
pos.erase(now);
}
}
puts("Yes");
return 0;
}
ABC245F. Endless Walk:拓扑排序(绿)
显然,若一个点出度为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 2e5+10;
int n, m, cnt; vector<int> e[N];
bool st[N]; int deg[N];
void toposort() {
queue<int> q;
for (int i = 1; i <= n; ++i) if (!deg[i]) st[i] = 1, q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
for (auto v : e[u]) {
deg[v] --;
if (!deg[v]) st[v] = 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[v].push_back(u), deg[u] ++;
}
toposort();
for (int i = 1; i <= n; ++i) if (!st[i]) cnt ++;
printf("%d\n", cnt);
return 0;
}
ABC245G. Foreign Friends:最短路(蓝)
我们枚举颜色的每个二进制位,每次把当前二进制位上为
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pii;
const int N = 1e5+10;
int n, m, k, l, col[N]; vector<pii> e[N]; vector<int> start;
ll dist[N], ans[N]; bool st[N];
void dijkstra(int bit, int now) {
memset(dist, 0x7f, sizeof dist), memset(st, 0, sizeof st);
priority_queue<pii, vector<pii>, greater<pii>> q;
for (auto u : start) if (!(col[u]>>bit&1^now)) q.push({0, u}), dist[u] = 0;
while (q.size()) {
auto t = q.top(); q.pop();
int u = t.second; ll dis = t.first;
if (st[u]) continue; st[u] = 1;
if (col[u]>>bit&1^now) ans[u] = min(ans[u], dist[u]);
for (auto [v, w] : e[u]) {
if (dist[v] > dis+w)
dist[v] = dis+w, q.push({dist[v], v});
}
}
}
int main() {
int u, v, w;
scanf("%d%d%d%d", &n, &m, &k, &l);
for (int i = 1; i <= n; ++i) scanf("%d", &col[i]);
for (int i = 1; i <= l; ++i) scanf("%d", &u), start.push_back(u);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &u, &v, &w);
e[u].push_back({v, w}), e[v].push_back({u, w});
}
memset(ans, 0x7f, sizeof ans);
for (int i = 0; 1<<(i-1) <= k; ++i) dijkstra(i, 0), dijkstra(i, 1);
for (int i = 1; i <= n; ++i) printf("%lld ", ans[i]>=1e18?-1:ans[i]); puts("");
return 0;
}
10.10
ABC244D. Swap Hats:枚举(红)
猜结论:若 No
;否则输出 Yes
。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int cnt; char s[5], t[5];
int main() {
cin >> s[1] >> s[2] >> s[3] >> t[1] >> t[2] >> t[3];
for (int i = 1; i <= 3; ++i) if (s[i] == t[i]) cnt ++;
if (cnt == 1) puts("No"); else puts("Yes");
return 0;
}
ABC244E. King Bombee:dp(黄)
令
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 2010, P = 998244353;
int n, m, k, s, t, x, f[N][N][2];
vector<int> e[N];
int main() {
scanf("%d%d%d%d%d%d", &n, &m, &k, &s, &t, &x);
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);
}
f[0][s][0] = 1;
for (int i = 1; i <= k; ++i) {
for (int j = 1; j <= n; ++j) {
for (auto u : e[j]) {
if (u == x) (f[i][j][0] += f[i-1][u][1]) %= P, (f[i][j][1] += f[i-1][u][0]) %= P;
else (f[i][j][0] += f[i-1][u][0]) %= P, (f[i][j][1] += f[i-1][u][1]) %= P;
}
}
}
printf("%d\n", f[k][t][0]);
return 0;
}
ABC245F. Shortest Good Path:bfs(绿)
我们令一个状态
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 20, M = 1<<17;
int n, m, g[N][N]; ll ans;
int f[N][M];
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v; scanf("%d%d", &u, &v);
g[u][v] = g[v][u] = 1;
}
memset(f, 0x3f, sizeof f);
queue<pii> q; q.push({0, 0}), f[0][0] = 0;
while (q.size()) {
auto t = q.front(); q.pop();
int u = t.first, now = t.second;
for (int v = 1; v <= n; ++v) {
if (g[u][v] || !u) {
int to = now ^ (1 << v-1);
if (f[v][to] == 0x3f3f3f3f) f[v][to] = f[u][now]+1, q.push({v, to});
}
}
}
for (int i = 1; i < (1<<n); ++i) {
int now = 0x3f3f3f3f;
for (int j = 1; j <= n; ++j) now = min(now, f[j][i]);
ans += now;
}
printf("%lld\n", ans);
return 0;
}
ABC245G. Construct Good Path:构造(蓝)
直接用 dfs 序建树然后模拟即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5+10;
int n, m, cnt[N], sch[N], s[N]; bool st[N];
vector<int> e[N], ee[N], res;
bool dfs(int u) {
bool now = s[u]; st[u] = 1;
for (auto v : e[u]) {
if (st[v]) continue;
now |= dfs(v), ee[u].push_back(v);
}
return sch[u]=now;
}
void get_path(int u, int fa) {
res.push_back(u), cnt[u] ^= 1;
for (auto v : ee[u]) {
if (v == fa || !sch[v]) continue;
get_path(v, u); res.push_back(u), cnt[u] ^= 1;
}
if (cnt[u] ^ s[u]) {
if (u != 1) res.push_back(fa), res.push_back(u), cnt[fa] ^= 1, cnt[u] ^= 1;
else res.pop_back();
}
}
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 = 1; i <= n; ++i) scanf("%1d", &s[i]);
dfs(1), get_path(1, 0);
printf("%d\n", res.size());
for (auto x : res) printf("%d ", x); puts("");
return 0;
}
10.11
ABC243D. Moves on Binary Tree:模拟(黄)
由于结果在 long long
范围内,将原数转化为二进制后用 vector
模拟即可。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
#define int long long
const int N = 1e6+10;
int n, x, res; char s[N];
vector<int> nums;
signed main() {
cin >> n >> x >> s+1;
while (x) {
if (x & 1) nums.push_back(1);
else nums.push_back(0);
x >>= 1;
}
reverse(nums.begin(), nums.end());
for (int i = 1; i <= n; ++i) {
if (s[i] == 'U') nums.pop_back();
else if (s[i] == 'L') nums.push_back(0);
else nums.push_back(1);
}
reverse(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); ++i) res += (1ll<<i) * nums[i];
cout << res << '\n';
return 0;
}
ABC243E. Edge Deletion:Floyd(绿)
Floyd 预处理两点间最短路,过程中计算是否存在一条路径
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
#define int long long
const int N = 310;
int n, m, cnt, f[N][N]; bool check[N][N];
struct Edge {
int u, v, w;
};
vector<Edge> edge;
void floyd() {
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
if (f[i][j] >= f[i][k]+f[k][j] && i != k && j != k)
f[i][j] = f[i][k]+f[k][j], check[i][j] = 1;
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(f, 0x3f, sizeof f);
for (int i = 1; i <= n; ++i) f[i][i] = 0;
for (int i = 1; i <= m; ++i) {
int u, v, w; cin >> u >> v >> w;
f[u][v] = f[v][u] = w, edge.push_back({u, v, w});
}
floyd();
for (auto [u, v, w] : edge) {
if (check[u][v])
cnt ++;
}
cout << cnt << '\n';
return 0;
}
ABC243F. Lottery:概率 dp,数学(蓝)
抽
那么这些数一共能组成
考虑 dp,令
最终答案为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 55, P = 998244353;
int n, m, k, sum, inv, p[N], fac[N], infac[N], pow[N][N], f[N][N][N];
int power(int a, int b) {
int res = 1;
while (b) {
if (b & 1) res = 1ll * res * a % P;
a = 1ll * a * a % P, b >>= 1;
}
return res;
}
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &p[i]), sum += p[i];
inv = power(sum, P-2);
for (int i = 1; i <= n; ++i) {
p[i] = 1ll * p[i] * inv % P;
for (int j = 1; j <= k; ++j) pow[i][j] = power(p[i], j);
}
fac[0] = 1; for (int i = 1; i <= k; ++i) fac[i] = 1ll * fac[i-1] * i % P;
infac[k] = power(fac[k], P-2); for (int i = k-1; i >= 0; --i) infac[i] = 1ll * infac[i+1] * (i+1) % P;
for (int i = 0; i <= n; ++i) f[i][0][0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= m; ++j)
for (int t = 1; t <= k; ++t) {
f[i][j][t] = f[i-1][j][t];
for (int x = 1; x <= t-j+1; ++x)
(f[i][j][t] += 1ll * f[i-1][j-1][t-x] * pow[i][x] % P * infac[x] % P) %= P;
}
}
printf("%d\n", 1ll*f[n][m][k]*fac[k]%P);
return 0;
}
10.12
P2325 [SCOI2005] 王室联邦:dfs,树分块(蓝)
考虑如下的构造方法:
- dfs 整棵树,处理每个节点时,将其一部分子节点分块,将未被分块的子节点返回到上一层;
- 枚举
的每个子节点 ,递归处理完子树后,将每个子节点返回的未被分块的节点添加至集合 中,若 就将 作为一个新的块并将 作为省会,然后清空 。 - 处理完所有子树后,将
也加入 中。显然有 。
由于
最后,
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int N = 1010;
int n, B, idx, root[N], id[N]; vector<int> e[N];
stack<int> s;
void dfs(int u, int fa) {
int now = s.size();
for (auto v : e[u]) {
if (v == fa) continue; dfs(v, u);
if (s.size()-now >= B) {
root[++ idx] = u;
while (s.size() > now) id[s.top()] = idx, s.pop();
}
}
s.push(u);
}
int main() {
scanf("%d%d", &n, &B);
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, 0);
if (!idx) root[++ idx] = 1;
while (s.size()) id[s.top()] = idx, s.pop();
printf("%d\n", idx);
for (int i = 1; i <= n; ++i) printf("%d ", id[i]); puts("");
for (int i = 1; i <= idx; ++i) printf("%d ", root[i]); puts("");
return 0;
}
ABC243G. Sqrt:dp,数学(蓝)
令
那么:
注意此题开根号时要用 long double
。时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 6e4+10;
#define int long long
int T, n, f[N], s1[N], s2[N];
signed main() {
scanf("%lld", &T);
f[1] = 1;
for (int i = 2; i <= 60000; ++i) {
for (int j = 1; j <= i/j; ++j)
f[i] += f[j];
}
for (int i = 1; i <= 60000; ++i) s1[i] = s1[i-1]+f[i], s2[i] = s2[i-1]+f[i]*i*i;
while (T -- ) {
scanf("%lld", &n);
int x = pow((long double)n, 0.5), y = pow((long double)n, 0.25);
printf("%lld\n", (x+1)*s1[y]-s2[y]);
}
return 0;
}
10.15
ABC324A. Same:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n, a[110];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 2; i <= n; ++i) if (a[i] != a[i-1]) puts("No"), exit(0);
puts("Yes");
return 0;
}
ABC324B. 3-smooth Numbers:枚举(红)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
long long n;
int main() {
cin >> n;
while (n % 2 == 0) n /= 2;
while (n % 3 == 0) n /= 3;
if (n > 1) puts("No"); else puts("Yes");
return 0;
}
ABC324C. Error Correction:枚举(橙)
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 5e5+10;
int n; string s[N];
vector<int> nums;
bool check(int k) {
int siz = s[0].size(), nsiz = s[k].size();
if (siz == nsiz) {
bool check = 1;
for (int i = 0; i < siz; ++i) {
if (!check && s[0][i] != s[k][i]) return 0;
if (check && s[0][i] != s[k][i]) check = 0;
}
} else if (siz == nsiz+1) {
bool check = 1;
for (int i = 0, j = 0; i < siz; ++i, ++j) {
if (!check && s[0][i] != s[k][j]) return 0;
if (check && s[0][i] != s[k][j]) check = 0, ++i;
if (s[0][i] != s[k][j]) return 0;
}
} else if (siz == nsiz-1) {
bool check = 1;
for (int i = 0, j = 0; i < siz; ++i, ++j) {
if (!check && s[0][i] != s[k][j]) return 0;
if (check && s[0][i] != s[k][j]) check = 0, ++j;
if (s[0][i] != s[k][j]) return 0;
}
} else return 0;
return 1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> s[0];
for (int i = 1; i <= n; ++i) cin >> s[i];
for (int i = 1; i <= n; ++i) if (check(i)) nums.push_back(i);
printf("%d\n", nums.size()); for (auto x : nums) printf("%d ", x); puts("");
return 0;
}
ABC324D. Square Permutation:枚举(黄)
直接枚举排列会超时。发现
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
#define int long long
int n, ans; char s[15]; vector<int> A, B;
bool cmp() {
for (int i = 0; i < A.size(); ++i) if (A[i] != B[i]) return 0;
return 1;
}
signed main() {
cin >> n >> s+1;
for (int i = 1; i <= n; ++i) A.push_back(s[i]-'0'); sort(A.begin(), A.end());
for (int i = 0; i <= 4000000; ++i) {
int x = i*i;
B.clear();
for (int j = 1; j < n; ++j) B.push_back((int)(x/pow(10, j-1))%10);
B.push_back(x/pow(10, n-1)); sort(B.begin(), B.end());
if (cmp()) ans ++;
}
printf("%lld\n", ans);
return 0;
}
ABC324E. Joint Two Strings:双指针,桶,枚举(黄)
双指针处理出
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 5e5+10;
int n, pre[N], suf[N], num[N]; ll ans;
string s[N];
int solve(string &A, string &B) {
int i = 0, j = -1;
while (i < A.size() && j+1 < B.size()) {
if (A[i] == B[j+1]) j ++;
i ++;
}
return j+1;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> s[0];
for (int i = 1; i <= n; ++i) cin >> s[i];
for (int i = 1; i <= n; ++i) pre[i] = solve(s[i], s[0]), reverse(s[i].begin(), s[i].end());
reverse(s[0].begin(), s[0].end());
for (int i = 1; i <= n; ++i) suf[i] = solve(s[i], s[0]), num[suf[i]] ++;
for (int i = s[0].size()-1; ~i; --i) num[i] += num[i+1];
for (int i = 1; i <= n; ++i) ans += num[s[0].size()-pre[i]];
printf("%lld\n", ans);
return 0;
}
ABC324F. Beautiful Path:01分数规划,二分,bfs(拓扑排序),dp(绿)
假设我们依次经过边
的最大值。这是一个经典的 01 规划问题,可以通过二分解决。
假设
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef pair<int, double> pid;
const int N = 2e5+10;
const double eps = 1e-12, INF = 1e9;
struct Edge {
int u, v, b, c;
};
int n, m, deg[N]; double f[N];
vector<pid> e[N]; vector<Edge> Edg;
void bfs() {
for (int i = 2; i <= n; ++i) f[i] = -INF;
queue<int> q;
for (int i = 1; i <= n; ++i) if (!deg[i]) q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
for (auto [v, w] : e[u]) {
deg[v] --, f[v] = max(f[v], f[u]+w);
if (!deg[v]) q.push(v);
}
}
}
bool check(double x) {
for (int i = 1; i <= n; ++i) e[i].clear();
for (auto [u, v, b, c] : Edg) {
double w = b - x*c;
e[u].push_back({v, w}), deg[v] ++;
}
bfs();
return (f[n] >= 0);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i) {
int u, v, b, c; scanf("%d%d%d%d", &u, &v, &b, &c);
Edg.push_back({u, v, b, c});
}
double l = 0, r = 2e9;
while (l+eps < r) {
double mid = (l + r) / 2;
if (check(mid)) l = mid;
else r = mid;
}
printf("%.12f\n", l);
return 0;
}
10.16
P2553 [AHOI2001] 多项式乘法:模拟(蓝)
难点在读入字符串。时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 80;
int a[N], b[N], c[N];
string s;
int main() {
while (getline(cin, s)) {
memset(a, 0, sizeof a), memset(b, 0, sizeof b), memset(c, 0, sizeof c);
bool ismul = 0;
for (auto c : s) if (c == '*') ismul = 1;
if (!ismul) {
for (auto c : s) if (isdigit(c) || c == '+' || c == '*') cout << c;
cout << '\n';
continue;
} else {
bool ispow = 0; int i, num = 0, power = 0;
for (i = 0; i < s.size(); ++i) {
char c = s[i];
if (!isdigit(c)) {
if (c == '(' || c == '*') continue;
else if (c == '^') ispow = 1;
else if (c == '+' || c == ')') {
a[power] += num, num = 0, power = 0, ispow = 0;
if (c == ')') break;
}
} else {
if (!ispow) num = num * 10 + c - '0';
else power = power * 10 + c - '0';
}
}
for (i = i+1 ; i < s.size(); ++i) {
char c = s[i];
if (!isdigit(c)) {
if (c == '(' || c == '*') continue;
else if (c == '^') ispow = 1;
else if (c == '+' || c == ')') {
b[power] += num, num = 0, power = 0, ispow = 0;
if (c == ')') break;
}
} else {
if (!ispow) num = num * 10 + c - '0';
else power = power * 10 + c - '0';
}
}
for (int i = 0; i <= 30; ++i) {
for (int j = 0; j <= 30; ++j)
c[i+j] += a[i]*b[j];
}
bool check = 0;
for (int i = 60; i >= 0; --i) {
if (c[i]) {
if (check) printf("+");
printf("%d", c[i]);
if (i) printf("a^%d", i);
check = 1;
}
}
puts("");
}
}
return 0;
}
ABC242D. ABC Transform:模拟,递归,思维(黄)
令
(
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 1e5+10;
int q; char s[N];
char f(ll t, ll k) {
if (!t) return s[k];
if (k == 1) return 'A' + (s[1]-'A'+t) % 3;
return 'A' + (f(t-1, (k+1)/2)-'A'+(k&1^1)+1) % 3;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> s+1 >> q;
ll t, k;
while (q -- ) {
cin >> t >> k;
cout << f(t, k) << '\n';
}
return 0;
}
ABC242E. (∀x∀):组合数学(绿)
由于是回文串,我们只考虑前
最后还需要考虑
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e6+10, P = 998244353;
int T, n, power[N]; char s[N];
void solve() {
int ans = 0; bool check = 1;
cin >> n >> s+1;
for (int i = 1; i <= (n+1)/2; ++i) (ans += 1ll * (s[i]-'A') * power[(n+1)/2-i] % P) %= P;
for (int i = n/2; i >= 1; --i) {
if (s[i] < s[n-i+1]) {check = 1; break;}
else if (s[i] > s[n-i+1]) {check = 0; break;}
}
cout << (ans+check)%P << '\n';
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
power[0] = 1;
for (int i = 1; i <= 1000000; ++i) power[i] = 26ll * power[i-1] % P;
cin >> T;
while (T -- ) solve();
return 0;
}
10.20
P1229 遍历问题:树,数学(黄)
注意到对于一个节点
根据前后序遍历的性质,若
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 5010;
int n, cnt;
char pre[N], suf[N];
int main() {
cin >> pre+1 >> suf+1; n = strlen(pre+1);
for (int i = 1; i <= n; ++i) {
for (int j = 2; j <= n; ++j)
if (pre[i] == suf[j] && pre[i+1] == suf[j-1])
cnt ++;
}
cout << pow(2, cnt) << '\n';
return 0;
}
P5186 [COCI2009-2010#4] OGRADA:单调队列(绿)
(双倍经验:P7697 [COCI2009-2010#4] OGRADA)
对于第一问,首先求出每一次刷最大能刷的值,这个可以用单调队列求;然后对于每个栅栏,求出每个覆盖它的部分的最大值,也可以用单调队列求。用总和减去每个栅栏能刷到的最大值之和即可。
对于第二问,假设区间
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <deque>
using namespace std;
const int N = 1e6+10;
int n, x, cnt; long long sum, ans;
int h[N], p[N], col[N];
int main() {
scanf("%d%d", &n, &x);
for (int i = 1; i <= n; ++i) scanf("%d", &h[i]), sum += h[i];
// 预处理每一段区间的最小值
deque<int> q;
for (int i = 1; i < x; ++i) {
while (q.size() && h[q.back()] > h[i]) q.pop_back();
q.push_back(i);
}
for (int i = x; i <= n; ++i) {
while (q.size() && q.front() < i-x+1) q.pop_front();
while (q.size() && h[q.back()] > h[i]) q.pop_back();
q.push_back(i), p[i-x+1] = h[q.front()];
}
// 找到每一次上色的最大值
q.clear();
for (int i = 1; i <= n; ++i) {
while (q.size() && q.front() < i-x+1) q.pop_front();
while (q.size() && i <= n-x+1 && p[q.back()] < p[i]) q.pop_back();
if (i <= n-x+1) q.push_back(i);
ans += col[i] = p[q.front()];
}
printf("%lld\n", sum-ans);
// 找到连续段
int siz = 0;
for (int i = 1; i <= n; ++i) {
if (col[i] != col[i-1]) cnt += ceil((double)siz/x), siz = 1;
else siz ++;
}
cnt += ceil((double)siz/x);
printf("%d\n", cnt);
return 0;
}
P1841 [JSOI2007] 重要的城市:Floyd,dp(蓝)
在计算最短路的同时,计算最短路的条数。对于点
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 210;
int n, m, cnt, g[N][N], num[N][N];
vector<int> e[N];
void floyd() {
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) {
if (g[i][j] > g[i][k]+g[k][j]) g[i][j] = g[i][k]+g[k][j], num[i][j] = num[i][k]*num[k][j];
else if (g[i][j] == g[i][k]+g[k][j]) num[i][j] += num[i][k]*num[k][j];
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j)
g[i][j] = (i == j) ? 0 : 1e9;
}
for (int i = 1; i <= m; ++i) {
int u, v, w; scanf("%d%d%d", &u, &v, &w);
g[u][v] = g[v][u] = min(g[u][v], w), num[u][v] = num[v][u] = 1, e[u].push_back(v);
}
floyd();
for (int i = 1; i <= n; ++i) {
bool check = 0;
for (int j = 1; j <= n; ++j) {
for (int k = 1; k <= n; ++k) {
if (i == j || i == k || j == k) continue;
if (g[j][k] == g[j][i]+g[i][k] && num[j][k] == num[j][i]*num[i][k])
check = 1;
}
}
if (check) printf("%d ", i), cnt ++;
}
if (!cnt) puts("No important cities.");
return 0;
}
CF1100E. Andrew and Taxi:二分,拓扑排序(紫)
答案显然具有单调性,即若
如何判断
:这一部分边是不能调整方向的,可以通过拓扑排序判断是否有环。若有环则 一定不是合法解。 :这一部分边是可以调整方向的。若 的拓扑序大于 的拓扑序,则会产生环,需要把这条边反过来。
时间复杂度
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
const int N = 1e5+10;
struct Edge {
int u, v, w;
} edge[N];
int n, m, idx, deg[N], topo[N];
vector<int> e[N], ans;
bool toposort() {
idx = 0; queue<int> q; int cnt = 0;
for (int i = 1; i <= n; ++i) if (!deg[i]) q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
topo[u] = ++ idx, cnt ++;
for (auto v : e[u]) {
deg[v] --;
if (!deg[v]) q.push(v);
}
}
return (cnt == n);
}
bool check(int x) {
for (int i = 1; i <= n; ++i) e[i].clear(), deg[i] = 0;
for (int i = 1; i <= m; ++i) {
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
if (w > x) e[u].push_back(v), deg[v] ++;
}
if (!toposort()) return 0;
ans.clear();
for (int i = 1; i <= m; ++i) {
int u = edge[i].u, v = edge[i].v, w = edge[i].w;
if (w <= x && topo[u] > topo[v]) ans.push_back(i);
}
return 1;
}
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);
edge[i] = {u, v, w};
}
int l = 0, r = 1e9+1;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid+1;
}
printf("%d %d\n", l, ans.size());
for (auto u : ans) printf("%d ", u); puts("");
return 0;
}
P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G:有向图强连通分量(tarjan)(绿)
缩点后,若只存在一个出度为
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e4+10;
int n, m; vector<int> e[N];
int idx, top, cnt, dfn[N], low[N], stk[N], ins[N], c[N], siz[N], deg[N];
void tarjan(int u) {
dfn[u] = low[u] = ++ idx, stk[++ top] = u, ins[u] = 1;
for (auto v : e[u]) {
if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
cnt ++; int t;
do t = stk[top --], ins[t] = 0, c[t] = cnt, siz[cnt] ++;
while (u != t);
}
}
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);
}
for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; ++i) {
for (auto v : e[i]) {
if (c[i] != c[v])
deg[c[i]] ++;
}
}
int now = 0;
for (int i = 1; i <= cnt; ++i) {
if (!deg[i]) {
if (now) puts("0"), exit(0);
else now = i;
}
}
printf("%d\n", siz[now]);
return 0;
}
本文作者:Jasper08
本文链接:https://www.cnblogs.com/Jasper08/p/17778300.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步