2025/7/10 cw模拟赛题解
T1
你发现 \(N\) 的一位上的数字越小能够变大的概率就越大,那么就顺序选掉所有为 \(0\) 的数位,之后接着最后一个 \(0\) 的位置后面选 \(1\),以此类推。
#include <bits/stdc++.h>
//#define FILEIO
#define int long long
using namespace std;
constexpr int N = 2e5 + 10;
constexpr int mod = 998244353;
char s[N];
int n, num[N], sta[N], top, st, pw[N], ipw[N], ans;
int fpow(int x, int exp) {
int res = 1;
for (; exp; exp /= 2) {
if (exp & 1) {
res = res * x % mod;
}
x = x * x % mod;
}
return res;
}
void pre() {
pw[0] = ipw[0] = 1;
for (int i = 1; i <= 200000; i ++) {
pw[i] = pw[i - 1] * 10 % mod;
ipw[i] = fpow(pw[i], mod - 2);
}
}
signed main() {
#ifdef FILEIO
freopen ("opt.in", "r", stdin);
freopen ("opt.out", "w", stdout);
#endif
scanf ("%s", s + 1);
n = strlen(s + 1), st = 1;
for (int i = 1; i <= n; i ++) {
num[i] = s[i] ^ 48;
}
for (int ch = 0; ch < 10; ch ++) {
for (int i = st; i <= n; i ++) {
if (ch == num[i]) {
sta[++top] = 9 - ch;
st = i;
}
}
}
pre();
for (int i = 1; i <= top; i ++) {
ans = (ans + sta[i] * ipw[i] % mod) % mod;
}
printf ("%lld\n", ans);
return 0;
}
T2
点 分 治!
遍历预处理以当前根的子树节点的深度,你发现你想要 \(a_i \leq a_j\) 且路径长度最大,那么这玩意就是在值域上查 \(a_i\) 后缀最大的深度,直接树状数组维护。
需要注意的是你是以一种顺序遍历了子树更新答案,比如 \(i < j\),那么你还要反过来遍历一遍更新答案。
#include <bits/stdc++.h>
//#define FILEIO
#define fgetchar
#define int long long
#define pii pair<int,int>
#define mkp make_pair
using namespace std;
#ifdef fgetchar
char buf[1 << 23], *p1, *p2;
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++)
#endif
inline int read() {
int res = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) f = ch == '-' ? -1 : 1, ch = getchar();
while (isdigit(ch)) res = res * 10 + (ch ^ 48), ch = getchar();
return res * f;
}
constexpr int N = 200010;
constexpr int inf = 0x7f7f7f7f;
int n, a[N], cpy[N], len;
int tree[N];
int head[N], idx;
int siz[N], root, weight[N], sizt;
int sta[N], top;
bool rooted[N];
int ans[N];
vector<int> son;
vector<pii> node;
struct TreeNode {
int to, nxt;
} edge[N << 1];
inline void addedge(int u, int v) {
edge[idx].to = v;
edge[idx].nxt = head[u];
head[u] = idx ++;
}
inline void update(int p, int val) {
for (; p; p -= (p & (-p))) {
tree[p] = max(tree[p], val);
}
return;
}
inline int query(int p) {
int res = ~inf;
for (; p <= len; p += (p & (-p))) {
res = max(res, tree[p]);
}
return res;
}
inline void clear(int p) {
for (; p; p -= (p & (-p))) {
tree[p] = ~inf;
}
return;
}
void findHeavy(int u, int fth) {
siz[u] = 1, weight[u] = 0;
for (int i = head[u]; ~i; i = edge[i].nxt) {
int to = edge[i].to;
if (to != fth && !rooted[to]) {
findHeavy(to, u);
siz[u] += siz[to];
weight[u] = max(weight[u], siz[to]);
}
}
weight[u] = max(sizt - siz[u], weight[u]);
if (weight[u] < weight[root]) {
root = u;
}
}
void dfs(int u, int fth, int dist) {
node.push_back(mkp(u, dist));
for (int i = head[u]; ~i; i = edge[i].nxt) {
int to = edge[i].to;
if (to != fth && !rooted[to]) {
dfs(to, u, dist + 1);
}
}
}
void caculate(int rt) {
update(a[rt], 0);
sta[++top] = a[rt];
for (int to : son) {
node.clear();
dfs(to, rt, 1);
for (auto p : node) {
int x = p.first, y = p.second;
ans[x] = max(ans[x], query(a[x]) + y);
}
for (auto p : node) {
int x = p.first, y = p.second;
sta[++top] = a[x];
update(a[x], y);
}
}
ans[rt] = max(ans[rt], query(a[rt]));
while(top) {
clear(sta[top--]);
}
}
void solve(int rt) {
son.clear();
rooted[rt] = true;
for (int i = head[rt]; ~i; i = edge[i].nxt) {
if (!rooted[edge[i].to]) {
son.push_back(edge[i].to);
}
}
caculate(rt);
reverse(son.begin(), son.end());
caculate(rt);
for (int i = head[rt]; ~i; i = edge[i].nxt) {
int to = edge[i].to;
if (!rooted[to]) {
root = 0;
sizt = siz[to];
findHeavy(to, rt);
solve(root);
}
}
}
signed main() {
#ifdef FILEIO
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
#endif
n = read();
memset (head, -1, sizeof(head));
memset (tree, ~0x7f, sizeof(tree));
for (int i = 1; i <= n; i ++) {
a[i] = cpy[i] = read();
}
sort (cpy + 1, cpy + n + 1);
len = unique(cpy + 1, cpy + n + 1) - (cpy + 1);
for (int i = 1; i <= n; i ++) {
a[i] = lower_bound(cpy + 1, cpy + len + 1, a[i]) - cpy;
}
for (int i = 1; i < n; i ++) {
int u = read(), v = read();
addedge(u, v), addedge(v, u);
}
sizt = n;
weight[root = 0] = inf;
findHeavy(1, -1);
solve(root);
for (int i = 1; i <= n; i ++) {
printf ("%lld%c", ans[i], " \n"[i == n]);
}
return 0;
}
/*
5
4 2 2 3 1
1 2
1 3
3 4
3 5
*/
T3
神秘网络流。
你发现最后的差只有两种状态 \(\left|A_i - A_{i + 1}\right|\) 和 \(\left|A_i + A_{i + 1}\right|\),同时你发现这个状态有限制 \(\left|A_i - A_{i + 1}\right| \neq \left|A_j - A_{j + 1}\right|\),同时你又发现 \(\left|A_i - A_{i + 1}\right|\) 到 \(\left|A_i + A_{i + 1}\right|\) 有 \(1\) 的花费,那么考虑最小费用最大流。
所有边的流量都为 \(1\),如果最大流不为 \(n - 1\) 说明不满足限制。
建模就由 \(\left|A_i - A_{i + 1}\right|\) 向 \(\left|A_i + A_{i + 1}\right|\) 连费用为 \(1\) 的边,因为两种状态都可以作为最终状态,所以都向 \(T\) 连边无费用。
初始状态就是 \(\left|A_i - A_{i + 1}\right|\),由 \(S\) 向其连边无费用。
需要注意的是,一次修改相当于是修改两个点的状态,下一次增广另外一个与其配对的点又会算一次费用,所以最后答案是 \(\lceil\frac{cost}{2}\rceil\)。
#include <bits/stdc++.h>
//#define FILEIO
#define int long long
using namespace std;
constexpr int N = 200010;
constexpr int inf = 0x3f3f3f3f3f3f3f3f;
int n, a[N];
int S, T;
int num[N], cnt;
int lhs[N], rhs[N];
int head[N], idx;
int dis[N], cur[N];
bool vis[N], pass[N];
struct graph {
int v;
int w;
int c;
int nxt;
} e[N << 2];
void addedge(int u, int v, int w, int c) {
e[idx].v = v;
e[idx].w = w;
e[idx].c = c;
e[idx].nxt = head[u];
head[u] = idx ++;
}
void Addedge(int u, int v, int w, int c) {
addedge(u, v, w, c), addedge(v, u, 0, -c);
}
bool spfa() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
memcpy(cur, head, sizeof(head));
queue<int> q;
q.push(S);
dis[S] = 0;
vis[S] = true;
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; ~i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w, c = e[i].c;
if (dis[v] > dis[u] + c && w) {
dis[v] = dis[u] + c;
if (!vis[v]) {
q.push(v);
vis[v] = true;
}
}
}
}
return dis[T] != inf;
}
int dinic(int u, int maxflow) {
if (u == T)
return maxflow;
int sum = 0;
pass[u] = true;
for (int i = cur[u]; ~i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w, c = e[i].c;
if (!pass[v] && w && dis[v] == dis[u] + c) {
int t = dinic(v, min(maxflow, w));
e[i].w -= t;
maxflow -= t;
e[i ^ 1].w += t;
sum += t;
if (!maxflow)
break;
}
}
pass[u] = false;
if (!sum)
dis[u] = inf;
return sum;
}
signed main() {
#ifdef FILEIO
freopen("rev.in", "r", stdin);
freopen("rev.out", "w", stdout);
#endif
scanf ("%lld", &n);
for (int i = 1; i <= n; i ++) {
scanf ("%lld", &a[i]);
}
for (int i = 1; i < n; i ++) {
num[++cnt] = lhs[i] = abs(a[i] - a[i + 1]);
num[++cnt] = rhs[i] = abs(a[i] + a[i + 1]);
}
sort(num + 1, num + cnt + 1);
cnt = unique(num + 1, num + cnt + 1) - (num + 1);
for (int i = 1; i < n; i ++) {
lhs[i] = lower_bound(num + 1, num + cnt + 1, lhs[i]) - num;
rhs[i] = lower_bound(num + 1, num + cnt + 1, rhs[i]) - num;
}
S = cnt + 1, T = S + 1;
memset(head, -1, sizeof(head));
for (int i = 1; i <= cnt; i ++) {
Addedge(i, T, 1, 0);
}
for (int i = 1; i < n; i ++) {
Addedge(S, lhs[i], 1, 0);
Addedge(lhs[i], rhs[i], 1, 1);
}
int ansF = 0, ansC = 0;
while (spfa()) {
int F = dinic(S, inf);
ansF += F;
ansC += F * dis[T];
}
if (ansF != n - 1)
return puts("-1"), 0;
printf ("%lld\n", (ansC + 1) / 2);
return 0;
}
/*
4
1 3 0 2
*/

浙公网安备 33010602011771号