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
*/
posted @ 2025-07-14 16:51  xAlec  阅读(8)  评论(0)    收藏  举报