正睿 25 年联赛联合训练 Day 12

正睿 25 年联赛联合训练 Day 12

得分

T1 T2 T3 总分 排名
\(100\) \(100\) \(20\) \(220\) \(2/17\)

题解

T1 LIS

神秘找规律。写一个爆搜,发现最优方案就是在每一个 \(b_i\) 前依次放 \(m\sim b_i+1\),这样一定是最长的。判断一下 \(n\) 和序列的最长长度的关系然后输出方案即可。

#include <bits/stdc++.h>
#define int long long

using namespace std;

const int Maxn = 3e5 + 5;
const int Inf = 2e9;

int n, m, k, b[Maxn];
int sum = 0;
int a[Maxn];

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> k;
	for(int i = 1; i <= k; i++) cin >> b[i], sum += b[i];
	if(k * m - sum + k < n) cout << "No\n";
	else {
		cout << "Yes\n";
		int tot = 0, cnt = 0, lst = n - k;
		for(int i = 1; i <= k; i++) {
			cnt = m;
			while(lst > 0 && cnt > b[i]) {
				a[++tot] = cnt; cnt--; lst--;
			}
			a[++tot] = b[i];
		}
		for(int i = 1; i <= n; i++) {
			cout << a[i] << " ";
		}
	}
	return 0;
}

T2 图论

这个题标算被 \(O(n^4),O(n^3\log n)\) 踩爆了……

我们先考虑一个贪心做法,我们每次取出度数和最大的边,将其加入边集并更新 \(K\) 的最大值;然后将与这两个点相连的边的度数更新。显然上述过程可以用堆维护,复杂度是 \(O(n^3\log n)\) 的。

实际上我们有一个简单的优化,可以用桶排的思想,维护 \(2n\) 个队列,每次从最大的的队列里取出边然后更新下面的队列,这样做就可以做到 \(O(n^3)\) 了。但是实际运行效率较低。

代码没有写正解。

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 2e5 + 5;
const int Inf = 2e9;

int n, m;
int mat[505][505], dg[Maxn];
int res[505][505], deg[Maxn];

queue <int> q;
int vis[Maxn];
int bfs(int k) {
	for(int i = 1; i <= n; i++) {
		vis[i] = 1, q.push(i), deg[i] = dg[i];
		for(int j = 1; j <= n; j++) res[i][j] = mat[i][j];
	}
	int sum = m;
	vector <int> son;
	while(!q.empty()) {
		int x = q.front(); q.pop();
		vis[x] = 0;
		son.clear();
		for(int i = 1; i <= n; i++) {
			if(res[i][x] || i == x) continue;
			son.push_back(i);
		}
		sort(son.begin(), son.end(), [](const int &x, const int &y){return deg[x] > deg[y];});
		for(auto i : son) {
			if(deg[x] + deg[i] < k) break;
			deg[x]++, deg[i]++, res[x][i] = res[i][x] = 1;
			sum++;
			if(!vis[i]) q.push(i);
		}
	}
	if(sum != n * (n - 1) / 2) return 0;
	return 1;
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m;
	for(int i = 1; i <= m; i++) {
		int u, v; cin >> u >> v;
		mat[u][v] = mat[v][u] = 1;
		dg[u]++, dg[v]++;
	}
	int l = 0, r = 2 * n - 2, res = 0;
	while(l <= r) {
		int mid = (l + r) >> 1;
		if(bfs(mid)) res = mid, l = mid + 1;
		else r = mid - 1;
	}
	cout << res << '\n';
	return 0;
}

T3 防御

先考虑只有一个防御装置怎么办。我们可以求出每个点看不到的点的个数。定义一个点关于一条线段在 \(x\) 轴上的投影为,连接点和线段的两个端点,其与 \(x\) 轴两交点的线段。那么如果上面的点不能被下面的点看到,则下面的点的投影一定完全包含在上面的点的投影中。

这个已经可以直接用二维偏序做了,不过实际上还有更简单的方法。不难发现一个性质:对于所有在上面的点的投影,它们的 \(l,r\) 都是递增排序的。所以我们可以直接二分求出答案。复杂度 \(O(q\log n)\)

考虑有多个防御装置怎么办,考虑利用容斥的思路,用被一个挡住的减去被两个挡住的,再加上被三个挡住的……可以暴力枚举子集,然后用上面相同的办法去做;这个时候的投影就是若干个投影的交集。这样做的复杂度就是 \(O(2^m q(m+\log n))\) 的。

#include <bits/stdc++.h>

using namespace std;

typedef long double db;
const int Maxn = 2e5 + 5;
const db Inf = 1e18;

int n, m, q;

struct Point {
	int x, y;
}tar[Maxn], atk[Maxn];

struct Line {
	int l, r, y;
}dfs[Maxn];

int ans[Maxn];

db calc(db x1, db y1, db x2, db y2) {
	if(x1 == x2) return x1;
	else return x1 - y1 * (x2 - x1) / (y2 - y1);
}

db l[Maxn], r[Maxn];
int tot;

void work(int S, int zf) {
	tot = 0;
	for(int i = 1; i <= n; i++) {
		db pl = -Inf, pr = Inf;
		for(int j = 1; j <= m; j++) {
			if((S >> (j - 1)) & 1) {
				if(dfs[j].y >= tar[i].y) {
					pl = Inf, pr = -Inf; break;
				}
				pl = max(pl, calc(tar[i].x, tar[i].y, dfs[j].l, dfs[j].y));
				pr = min(pr, calc(tar[i].x, tar[i].y, dfs[j].r, dfs[j].y));
			}
		}
		if(pl <= pr) {
			tot++; l[tot] = pl, r[tot] = pr; 
		}
	}
	sort(l + 1, l + tot + 1); sort(r + 1, r + tot + 1);
	for(int i = 1; i <= q; i++) {
		db pl = -Inf, pr = Inf;
		for(int j = 1; j <= m; j++) {
			if((S >> (j - 1)) & 1) {
				pl = max(pl, calc(atk[i].x, atk[i].y, dfs[j].l, dfs[j].y));
				pr = min(pr, calc(atk[i].x, atk[i].y, dfs[j].r, dfs[j].y));
			}
		}
		if(pl <= pr) {
			int nl = lower_bound(r + 1, r + tot + 1, pr) - r;
			int nr = upper_bound(l + 1, l + tot + 1, pl) - l - 1;
			ans[i] += zf * max(nr - nl + 1, 0);
		}
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	cin >> n >> m >> q;
	for(int i = 1; i <= n; i++) cin >> tar[i].x >> tar[i].y;
	for(int i = 1; i <= m; i++) cin >> dfs[i].l >> dfs[i].r >> dfs[i].y;
	for(int i = 1; i <= q; i++) cin >> atk[i].x >> atk[i].y;
	for(int i = 1; i < (1 << m); i++) {
		if(__builtin_popcount(i) & 1) work(i, 1);
		else work(i, -1);
	}
	for(int i = 1; i <= q; i++) {
		cout << n - ans[i] << '\n';
	}
	return 0;
}
posted @ 2025-03-15 08:16  UKE_Automation  阅读(27)  评论(0)    收藏  举报