[赛记] NOIP2024加赛7

镜的绮想 (mirror) 100pts

考虑 Θ(nm) 的做法,发现我们可以对于每一对实点和虚点求它们的“镜面”,然后得到 Θ(nm) 个“镜面”,发现这些直线只可能是形如 y=0.5x,xZ 的直线,所以我们直接乘 2,然后开个桶统计一下即可;

时间复杂度:Θ(nm)

点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, m;
bool vis[3000005];
vector<int> shi[3000005], xu[3000005], v;
int sum[5000005];
int main() {
	freopen("mirror.in", "r", stdin);
	freopen("mirror.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> m;
	int x, y;
	for (int i = 1; i <= n; i++) {
		cin >> x >> y;
		x += 1e6;
		y += 1e6;
		shi[x].push_back(y);
		if (!vis[x]) {
			vis[x] = true;
			v.push_back(x);
		}
	}
	for (int i = 1; i <= m; i++) {
		cin >> x >> y;
		x += 1e6;
		y += 1e6;
		xu[x].push_back(y);
		if (!vis[x]) {
			vis[x] = true;
			v.push_back(x);
		}
	}
	int ans = 0;
	for (int i = 0; i < v.size(); i++) {
		for (int j = 0; j < shi[v[i]].size(); j++) {
			for (int k = 0; k < xu[v[i]].size(); k++) {
				sum[(shi[v[i]][j] + xu[v[i]][k])]++;
				ans = max(ans, sum[(shi[v[i]][j] + xu[v[i]][k])]);
			}
		}
	}
	cout << ans;
	return 0;
}

万物有灵 (animism) -pts

赛时不会独立集,所以没写

考虑独立集就是一个图中的点集,其中任意两点没有边相连;

那么对于这棵树,我们发现,它的最大独立集是从最后一层开始,每隔一层选一层,最后选到根,因为每往下走,它的节点数只增不减;

这样朴素实现是 Θ(n) 的,考虑优化;

发现它有一个循环节,每 2k 次就会回来,所以我们先暴力把周期翻倍,可以考虑计算循环节的贡献 + 剩余贡献,后者直接暴力,前者可以考虑DP,设 fi 表示前 i 个循环节的贡献和,那么有转移 fi=a×fi1+f1,就是把前面的左移一位,最后再加上 f1

这样就可以矩阵加速,所以时间复杂度:Θ(logn+k)

细节有些多;

点击查看代码
#include <iostream>
#include <cstdio>
using namespace std;
long long nn, k, mod;
long long a[2000005];
struct Mat{
	long long a[5][5];
	int n, m;
	inline void clear() {
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				a[i][j] = 0;
			}
		}
	}
	inline void one() {
		for (int i = 1; i <= n; i++) a[i][i] = 1;
	}
	inline void rsize(int x, int y) {
		n = x;
		m = y;
	}
	inline Mat operator *(const Mat &A) const {
		Mat ans;
		ans.rsize(n, A.m);
		ans.clear();
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= A.m; j++) {
				for (int k = 1; k <= m; k++) {
					ans.a[i][j] = (ans.a[i][j] + a[i][k] * A.a[k][j] % mod) % mod;
				}
			}
		}
		return ans;
	}
};
Mat ksm(Mat A, long long b) {
	Mat ans;
	ans.rsize(2, 2);
	ans.clear();
	ans.one();
	while(b) {
		if (b & 1) ans = ans * A;
		A = A * A;
		b >>= 1;
	}
	return ans;
}
long long qpow(long long a, long long b) {
	long long ans = 1;
	while(b) {
		if (b & 1) ans = ans * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return ans;
}
int main() {
	freopen("animism.in", "r", stdin);
	freopen("animism.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> nn >> k >> mod;
	for (int i = 1; i <= k; i++) {
		cin >> a[i];
		a[i + k] = a[i];
	}
	k *= 2;
	if (nn <= k) {
		long long ans = 1;
		long long now = 1;
		if (!(nn & 1)) {
			ans = 1;
			for (int i = 1; i <= nn; i++) {
				now = now * a[i] % mod;
				if (!(i & 1)) ans = (ans + now) % mod;
			}
		} else {
			ans = 0;
			for (int i = 1; i <= nn; i++) {
				now = now * a[i] % mod;
				if (i & 1) ans = (ans + now) % mod;
			}
		}
		cout << ans;
		return 0;
	}
	long long w = 1, f = 0;
	for (int i = 1; i <= k; i++) {
		w = w * a[i] % mod;
		if (!(nn & 1)) {
			if (!(i & 1)) f = (f + w) % mod;
		} else {
			if (i & 1) f = (f + w) % mod;
		}
	}
	Mat B;
	B.rsize(2, 2);
	B.a[1][1] = w; B.a[1][2] = 1; B.a[2][1] = 0; B.a[2][2] = 1;
	long long res = (nn + (nn & 1)) / k;
	B = ksm(B, res - 1);
	long long now = qpow(w, res);
	long long ans = (B.a[1][1] * f % mod + B.a[1][2] * f % mod) % mod;
	if (!(nn & 1)) ans = (ans + 1) % mod;
	long long ret = nn % k;
	for (int i = 1; i <= ret; i++) {
		now = now * a[i] % mod;
		if (ret & 1) {
			if (i & 1) ans = (ans + now) % mod;
		} else {
			if (!(i & 1)) ans = (ans + now) % mod;
		}
	}
	cout << ans;
	return 0;
}

白石溪 (creek) 30pts

暴力DP还挂了15pts。。。

DP不好优化,考虑贪心;

我们钦定一开始全是蓝色,那么每次把一个蓝色变成红色,贡献为 a[i] - b[i] + (i - 1) * d + (n - i) * c - now * (d + c),其中 now 是现在有的(不算他自己)的红色个数,我们发现减号后面的一项在这个状态下是相同的,所以直接按 a[i] - b[i] + (i - 1) * d + (n - i) * c 排序选最大即可;

时间复杂度: Θ(nlogn)

点击查看代码
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
long long n, c, d;
long long f[2][1000005];
long long a[1000005], b[1000005];
priority_queue<long long> q;
int main() {
	freopen("creek.in", "r", stdin);
	freopen("creek.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n >> c >> d;
	long long ans = 0;
	for (int i = 1; i <= n; i++) {
		cin >> a[i] >> b[i];
		ans += b[i];
		q.push(a[i] - b[i] + (i - 1) * d + (n - i) * c);
	}
	long long now = 0;
	long long sum = ans;
	while(!q.empty()) {
		long long t = q.top();
		q.pop();
		sum += t - now * (d + c);
		ans = max(ans, sum);
		now++;
	}
	cout << ans;
	return 0;
}

上山岗 (uphill) 15pts

我们首先让人从小到大选山,如果他能选就选最后面的,可以发现,这样是可以得到一个最大解的,这个直接线段树二分即可解决;

考虑将大数前移,不难发现,如果这个数在前一步操作中匹配上了,那么我们只能将其移到没有匹配的山去匹配(不会出现和小数互换的情况,因为如果能换,这个位置就是较小的那个数了)。如果没有匹配,考虑他能不能将一个小数换掉,或者去一个空位上(前面大的数留下来的),两者取最大即可;

开两棵线段树,然后线段树二分即可解决这个问题,时间复杂度: Θ(nlogn)

点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
int n;
int a[500005], b[500005], pos[500005], ans[500005];
bool vis[500005];
vector<int> v;
namespace SEG{
	inline int ls(int x) {
		return x << 1;
	}
	inline int rs(int x) {
		return x << 1 | 1;
	}
	struct sss{
		int l, r, mi;
	}tr[3000005];
	inline void push_up(int id) {
		tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
	}
	void bt(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		if (l == r) {
			tr[id].mi = a[l];
			return;
		}
		int mid = (l + r) >> 1;
		bt(ls(id), l, mid);
		bt(rs(id), mid + 1, r);
		push_up(id);
	}
	void bt1(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		if (l == r) {
			if (!vis[l]) tr[id].mi = a[l];
			else tr[id].mi = 2e9;
			return;
		}
		int mid = (l + r) >> 1;
		bt1(ls(id), l, mid);
		bt1(rs(id), mid + 1, r);
		push_up(id);
	}
	int ask(int id, int d) {
		if (tr[id].l == tr[id].r) return tr[id].l;
		if (tr[rs(id)].mi < d) return ask(rs(id), d);
		else return ask(ls(id), d);
	}
	int askk(int id, int d) {
		if (tr[id].l == tr[id].r) return tr[id].l;
		if (tr[ls(id)].mi < d) return askk(ls(id), d);
		else return askk(rs(id), d);
	}
	void del(int id, int pos) {
		if (tr[id].l == tr[id].r) {
			tr[id].mi = 2e9;
			return;
		}
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) del(ls(id), pos);
		else del(rs(id), pos);
		push_up(id);
	}
	void add(int id, int pos) {
		if (tr[id].l == tr[id].r) {
			tr[id].mi = a[tr[id].l];
			return;
		}
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) add(ls(id), pos);
		else add(rs(id), pos);
		push_up(id);
	}
}
namespace seg{
	inline int ls(int x) {
		return x << 1;
	}
	inline int rs(int x) {
		return x << 1 | 1;
	}
	struct sss{
		int l, r, mi;
	}tr[3000005];
	inline void push_up(int id) {
		tr[id].mi = min(tr[ls(id)].mi, tr[rs(id)].mi);
	}
	void bt(int id, int l, int r) {
		tr[id].l = l;
		tr[id].r = r;
		if (l == r) {
			if (vis[l]) tr[id].mi = a[l];
			else tr[id].mi = 2e9;
			return;
		}
		int mid = (l + r) >> 1;
		bt(ls(id), l, mid);
		bt(rs(id), mid + 1, r);
		push_up(id);
	}
	void del(int id, int pos) {
		if (tr[id].l == tr[id].r) {
			tr[id].mi = 2e9;
			return;
		}
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) del(ls(id), pos);
		else del(rs(id), pos);
		push_up(id);
	}
	int ask(int id, int d) {
		if (tr[id].l == tr[id].r) return tr[id].l;
		if (tr[ls(id)].mi < d) return ask(ls(id), d);
		else return ask(rs(id), d);
	}
	int askk(int id, int pos) {
		if (tr[id].l == tr[id].r) return tr[id].mi;
		int mid = (tr[id].l + tr[id].r) >> 1;
		if (pos <= mid) return askk(ls(id), pos);
		else return askk(rs(id), pos);
		push_up(id);
	}
}
int main() {
	freopen("uphill.in", "r", stdin);
	freopen("uphill.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	SEG::bt(1, 1, n);
	for (int i = 1; i <= n; i++) {
		cin >> b[i];
	}
	sort(b + 1, b + 1 + n);
	for (int i = 1; i <= n; i++) {
		if (SEG::tr[1].mi >= b[i]) continue;
		pos[i] = SEG::ask(1, b[i]);
		vis[pos[i]] = true;
		SEG::del(1, pos[i]);
	}
	seg::bt(1, 1, n);
	SEG::bt1(1, 1, n);
	for (int i = n; i >= 1; i--) {
		int p = seg::askk(1, pos[i]);
		if (p == 2e9) {
			if (seg::tr[1].mi >= b[i]) {
				v.push_back(b[i]);
				continue;
			}
			int now = seg::ask(1, b[i]);
			int ma = SEG::askk(1, 1e9);
			if (now < ma) {
				ans[now] = b[i];
				seg::del(1, now);
			} else {
				ans[ma] = b[i];
				SEG::del(1, ma);
			}
		} else {
			SEG::add(1, pos[i]);
			seg::del(1, pos[i]);
			int now = SEG::askk(1, b[i]);
			ans[now] = b[i];
			SEG::del(1, now);
		}
	}
	int now = 0;
	for (int i = 1; i <= n; i++) {
		if (!ans[i]) {
			cout << v[now] << ' ';
			now++;
		} else cout << ans[i] << ' ';
	}
	return 0;
}
posted @   Peppa_Even_Pig  阅读(25)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示