20250729 杂题

GDCPC 2024 图

图片

首先n-1让然想到树,如果能弄出来k个生成树,每个生成树uv都有路径就好了。

每次加入边的时候二分前缀第一个(u,v)不连通的生成树加入即可,统计答案每棵树搜一遍就行。


AGC016E Poor Turkeys

图片

考虑x和y如果必须吃一个肯定不行。

如果x和i,y和i必须吃一个也不行

x-i i-j j-k k-y 显然也不行。

也就是说两个集合有交就不行。

枚举每一对 𝑖, 𝑗,检查是否满足 𝑆𝑖 ∩ 𝑆𝑗 = ∅。总时间复杂度 𝑂(𝑛𝑚 + 𝑛3)。

我感觉这个很自然,分讨可以看课件:

图片


PA 2024 Desant 3

暴力是一个指数级的,直接枚举初始状态。

因为模数是2

(其实这是一个套路)

考虑搜索,虽然是指数级的,但是我们发现x 的后继有两种可能,而且递归计算这两种后继的贡献的奇偶性一定相等(我们并不关心它们具体贡献了什么,而只关心它们的奇偶性到底相等不相等),那么我们就可以直接忽略掉这两个后继,不计算它们。

就比如x和y都是问号,其实后继状态一模一样(如果x和y不同,从问号看),所以他们对答案没有影响,可以不搜。只需要考虑都为0或者都为1的即可,对于有一个不确定的,如果问号在 y 上且 ax​=0 就忽略掉,否则 ax​=1。此时考虑 ay​ 的取值,要么为 0 要么为 1。如果是 0 那么就交换,否则不交换。看起来产生了两个新状态,但其实是一个。我们的后继状态一定 ax′​=ay​,ay′​=1,于是只产生了唯一的后继状态,也就是 x 位置是问号且 ay​=1。问号在 x 上是对称的,也只有一个后继状态。

至于这样做,显然有一个漏洞是最后仍然会留问号,其实不然。

最后的问号你可以直接贪心。

不要显示传递,会被卡常,拷贝开销极大。

#include <bits/stdc++.h>
using namespace std;
#define int long long

int n, m;
vector<int> l, r;
int ans[42];

void dfs(int u, vector<int> &now) {
	if (u == m) {
		int cnt = 0;
		for (int i = 1; i <= n; i++) cnt += (now[i] == 1);
		
		for (int i = 1; i <= n; i++) {
			if (now[i] == 0) continue;
			int v = 0;
			for (int j = i; j <= n; j++) {
				if (now[j] == 0) break;
				if ((v += (now[j] == 1)) == cnt)
					ans[j - i + 1] ^= 1;
			}
		}
		return;
	}
	
	int x = l[u], y = r[u];
	if (now[x] == -1 && now[y] == -1) {
		// 尝试 a[x] = a[y] = 0
		now[x] = now[y] = 0;
		dfs(u + 1, now);
		now[x] = now[y] = -1; // 回溯
		
		// 尝试 a[x] = a[y] = 1
		now[x] = now[y] = 1;
		dfs(u + 1, now);
		now[x] = now[y] = -1; // 回溯
		
	} else if (now[x] == -1 || now[y] == -1) {
		if (now[x] == 0 || now[y] == 1) {
			dfs(u + 1, now);
		} else if (now[x] == 1) {
			int old_x = now[x], old_y = now[y];
			now[x] = -1; now[y] = 1;
			dfs(u + 1, now);
			now[x] = old_x; now[y] = old_y;
		} else if (now[y] == 0) {
			int old_x = now[x], old_y = now[y];
			now[x] = 0; now[y] = -1;
			dfs(u + 1, now);
			now[x] = old_x; now[y] = old_y;
		}
	} else {
		if (now[x] == 1 && now[y] == 0) {
			swap(now[x], now[y]);
			dfs(u + 1, now);
			swap(now[x], now[y]); // 回溯
		} else {
			dfs(u + 1, now);
		}
	}
}


int32_t main() {
	ios::sync_with_stdio(0);
	cin >> n >> m;
	l.resize(m);
	r.resize(m);
	for (int i = 0; i < m; i++) {
		cin >> l[i] >> r[i];
	}
	
	vector<int> init(n + 1, -1); 
	dfs(0, init);
	
	for (int i = 1; i <= n; i++) {
		cout << ans[i] << ' ';
	}
	cout << '\n';
	return 0;
}

所以复杂度就从2n变成了2(n/2)。


京都观光

图片

结论,使用单调栈维护所有横着和竖着斜率增加的点,然后从(0,0),每次贪心选斜率小的,一定是最优的。

感性理解:这样的话,就避免了“反悔”,也就是说为了当前的小代价选择了后面的大代价(即为斜率先减小后增加),我们要杜绝这样的选择,不会后悔,这样复杂度和正确性都能保证。

理性证明:
图片


posted @ 2025-07-29 20:18  Dreamers_Seve  阅读(9)  评论(0)    收藏  举报