2026.07.29 模拟赛 比赛总结

2026.07.29 模拟赛 比赛总结

T1 T2 T3 T4 sum rk
100 AC 24 WA 24 RE 8 WA 156 18/78

打的最基础的部分分,莼菜。

T1 能量管理

观察发现不是很能 dp 状物,考虑贪心。

\(v_i\) 从大到小考虑每个活动,容易证明较大的 \(v_i\)\(i\) 尽可能填满是一定不劣的。于是得到贪心思路。

随意维护即可。我采用的是 set 维护。时间复杂度 \(O(n \log n)\)

LEWISAK 疑似有 \(O(n)\) 的分治做法。

点击查看代码
#include <bits/stdc++.h>
#define il inline
#define int long long

using namespace std;

bool Beg;
namespace Zctf1088 {
namespace IO {
	const int bufsz = 1 << 20;
	char ibuf[bufsz], *p1 = ibuf, *p2 = ibuf;
	#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, bufsz, stdin), p1 == p2) ? EOF : *p1++)
	il int read() {
		int x = 0; char ch = getchar(); bool t = 0;
		while (ch < '0' || ch > '9') {t ^= ch == '-'; ch = getchar();}
		while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
		return t ? -x : x;
	}
	char obuf[bufsz], *p3 = obuf, stk[50];
	#define flush() (fwrite(obuf, 1, p3 - obuf, stdout), p3 = obuf)
	#define putchar(ch) (p3 == obuf + bufsz && flush(), *p3++ = (ch))
	il void write(int x, bool t = 0) {
		int top = 0;
		x < 0 ? putchar('-'), x = -x : 0;
		do {stk[++top] = x % 10 | 48; x /= 10;} while(x);
		while (top) putchar(stk[top--]);
		t ? putchar(' ') : putchar('\n');
	}
	struct FL {
		~FL() {flush();}
	} fl;
}
using IO::read; using IO::write;
const int N = 1e4 + 10;
const int INF = 0x3f3f3f3f3f3f3f3f;
int E, R, n;
struct node {
	int p, v;
	il bool operator < (const node & c) const {
		return v != c.v ? v > c.v : p < c.p;
	}
} a[N];
struct nd {
	int l, r, val1, val2;
	il bool operator < (const nd c) const {
		return l < c.l;
	}
};
set<nd> st;
il int solve() {
	E = read(), R = read(), n = read();
	for (int i = 1; i <= n; i++) 
		a[i].p = i, a[i].v = read();
	if (R >= E) {
		int sum = 0;
		for (int i = 1; i <= n; i++) 
			sum += E * a[i].v;
		write(sum);
		return 0;
	}
	sort(a + 1, a + 1 + n);
	st.clear();
	st.insert({1, n, E, 0});
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		int p = a[i].p;
		nd tmp; tmp.l = p;
		set<nd>::iterator it = st.upper_bound(tmp); it--;
		nd c = *it;
		st.erase(it);
		int own = min(c.val1 + R * (p - c.l + 1), E);
		int kep = max(c.val2 - R * (c.r - p + 1), 0ll);
		ans += (own - kep) * a[i].v;
		if (c.l < p) st.insert({c.l, p - 1, c.val1, own});
		if (p < c.r) st.insert({p + 1, c.r, kep, c.val2});
	}
	write(ans);
	return 0;
}
signed main() {
	freopen("energy.in", "r", stdin);
	freopen("energy.out", "w", stdout);
	int qq = read();
	while (qq--) solve();
	return 0;
}}
bool End;
il void Usd() {cerr << "\nUse: " << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n";}
signed main() {
	Zctf1088::main();
	Usd();
	return 0;
}

T2 比赛配对

正解是高超图论建模。这里给出一种比较亲民的做法。

考虑正难则反,不妨令每个 \((A_{2i-1},A_{2i})\) 的代价都为 \(2\),现在对于一些组减去一些代价。具体地:

  1. \((x,x) \rightarrow (x,x)\)。花费 \(0\) 的代价,即减去 \(2\)
  2. \((x,x) \rightarrow(z,z)\)。花费 \(2\) 的代价,即减去 \(0\)
  3. \((x,y)\rightarrow (x,x)\)。花费 \(1\) 的代价,即减去 \(1\)
  4. \((x,y)\rightarrow (y,y)\)。花费 \(1\) 的代价,即减去 \(1\)
  5. \((x,y)\rightarrow (z,z)\)。花费 \(2\) 的代价,即减去 \(0\)

考虑对于每个 \(x\),所有原始的 \((x,x)\) 的组最多只能保留一个,且保留一个肯定是最优的。

于是只需要考虑形如 \((x,y)\) 的组。

考虑图论建模。考虑对于每个 \(x\) 建立一个虚点。考虑对于每个 \((x,y)\) 建一个点,并分别向 \(x\)\(y\) 的虚点连边。于是得到一张二分图。不难发现,该图的最大匹配即为最大的可减去的代价。

稍微卡常即可通过此题。此处使用 dinic 跑二分图最大匹配。

时间复杂度 \(O(n \sqrt n)\)

点击查看代码
#include <bits/stdc++.h>
#define il inline

using namespace std;

bool Beg;
namespace Zctf1088 {
namespace IO {
	const int bufsz = 1 << 20;
	char ibuf[bufsz], *p1 = ibuf, *p2 = ibuf;
	#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, bufsz, stdin), p1 == p2) ? EOF : *p1++)
	il int read() {
		int x = 0; char ch = getchar(); bool t = 0;
		while (ch < '0' || ch > '9') {t ^= ch == '-'; ch = getchar();}
		while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
		return t ? -x : x;
	}
	char obuf[bufsz], *p3 = obuf, stk[50];
	#define flush() (fwrite(obuf, 1, p3 - obuf, stdout), p3 = obuf)
	#define putchar(ch) (p3 == obuf + bufsz && flush(), *p3++ = (ch))
	il void write(int x, bool t = 0) {
		int top = 0;
		x < 0 ? putchar('-'), x = -x : 0;
		do {stk[++top] = x % 10 | 48; x /= 10;} while(x);
		while (top) putchar(stk[top--]);
		t ? putchar(' ') : putchar('\n');
	}
	il void wrt() {putchar('\n');}
	struct FL {
		~FL() {flush();}
	} fl;
}
using IO::read; using IO::write; using IO::wrt;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 10, M = 2e6;
int n, qq, typ, a[N];
int t[N], p[N], st[N], tot;
struct Edge {
	int nxt, to, w;
} edge[M];
int head[N], cur[N], etot = 1;
il void addEdge(int x, int y, int w) {
	edge[++etot] = {head[x], y, w};
	head[x] = etot;
}
il void addEdge1(int x, int y, int w) {
	addEdge(x, y, w);
	addEdge(y, x, 0);
}
int dis[N], S, T;
queue<int> q;
il bool bfs(int n) {
	for (int i = 1; i <= n; i++) cur[i] = head[i], dis[i] = 0;
	while (!q.empty()) q.pop();
	dis[S] = 1; q.push(S);
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int i = head[x]; i; i = edge[i].nxt) {
			int y = edge[i].to, w = edge[i].w;
			if (w > 0 && !dis[y]) {
				dis[y] = dis[x] + 1;
				if (y == T) return true;
				q.push(y);
			}
		}
	}
	return false;
}
il int dfs(int x, int flow) {
	if (x == T) return flow;
	int rest = flow;
	for (int i = cur[x]; i && rest; i = edge[i].nxt) {
		cur[x] = i;
		int y = edge[i].to, w = edge[i].w;
		if (w > 0 && dis[y] == dis[x] + 1) {
			int k = dfs(y, min(rest, w));
			if (k == 0) dis[y] = 0;
			rest -= k;
			edge[i].w -= k;
			edge[i ^ 1].w += k;
		}
	}
	return flow - rest;
}
il int dinic(int n, int s, int t) {
	S = s, T = t;
	int res = 0;
	while (bfs(n)) res += dfs(S, INF);
	return res;
}
int id[N], id1[N], id2[N], vis[N];
il int solve() {
	for (int i = 1; i <= etot; i++) edge[i] = {0, 0, 0};
	etot = 1, tot = 0;
	n = read();
	for (int i = 1; i <= n; i++) t[i] = p[i] = vis[i] = 0;
	for (int i = 1; i <= n + n + 2; i++) head[i] = 0;
	for (int i = 1; i <= 2 * n; i++) a[i] = read();
	int ss = n + n + 1, tt = n + n + 2;
	for (int i = 1; i <= 2 * n; i += 2) {
		if (a[i] == a[i + 1]) t[a[i]]++, p[a[i]] = 1;
		else {
			addEdge1(a[i], n + (i + 1) / 2, 1);		id1[i] = etot;
			addEdge1(a[i + 1], n + (i + 1) / 2, 1);	id2[i] = etot;
			addEdge1(n + (i + 1) / 2, tt, 1);
		}
	}
	int cnt = 0;
	for (int i = 1; i <= n; i++) {
		if (t[i] == 0) addEdge1(ss, i, 1), id[i] = etot;
		else cnt++;
	}
	write(n * 2 - cnt * 2 - dinic(n + n + 2, ss, tt));
	for (int i = 1; i <= n; i++) {
		if (t[i] == 0 && edge[id[i]].w != 0) p[i] = 1;
		if (!p[i]) st[++tot] = i;
	}
	for (int i = 1, j = 0; i <= 2 * n; i += 2) {
		if (a[i] == a[i + 1]) {
			if (!vis[a[i]]) write(a[i], 1), write(a[i + 1], 1), vis[a[i]] = 1;
			else j++, write(st[j], 1), write(st[j], 1);
		} else {
			if (edge[id1[i]].w != 0) write(a[i], 1), write(a[i], 1);
			else if (edge[id2[i]].w != 0) write(a[i + 1], 1), write(a[i + 1], 1);
			else j++, write(st[j], 1), write(st[j], 1);
		}
	}
	wrt();
	return 0;
}
signed main() {
	freopen("pair.in", "r", stdin);
	freopen("pair.out", "w", stdout);
	typ = read(), qq = read();
	while (qq--) solve();
	return 0;
}}
bool End;
il void Usd() {cerr << "\nUse: " << (&Beg - &End) / 1024.0 / 1024.0 << "MB " << (double)clock() * 1000.0 / CLOCKS_PER_SEC << "ms\n";}
signed main() {
	Zctf1088::main();
	Usd();
	return 0;
}

T3 清仓甩卖

这是一道纯 counting。

累了。剩下的先咕着。

posted @ 2026-08-01 17:43  Zctf1088  阅读(17)  评论(0)    收藏  举报