LibreOJ 6043 「雅礼集训 2017 Day7」蛐蛐国的修墙方案

发布时间 2023-07-03 17:31:47作者: lhzawa

根据 \(P_i\) 是个排列,那将 \(i\)\(P_i\) 进行连边之后不难发现图是由许多环构成的。
则若 \(i\)(\(P_i\) 则肯定为 )\(P_j = i\)\(j\) 肯定也为 ),否则就会出现度数为 \(2\) 的情况。
所以发现一个点与相邻两个点的状态是恰好相反的,即确定环上任意一点就可以确定环上整个点的状态,进一步还能发现环只能是偶环,若是奇环则无法构造出合法的方案。

那么对于 \(n = 40\),偶环最多有 \(20\) 个,直接枚举每个环状态判断即可,最坏时间复杂度 \(O(2^{\frac{n}{2}})\)\(n=100\) 还是过不了。

发现题目还有个性质没用到,整个括号序列合法,则对于每一位前缀 ( 的个数 \(\ge\) ) 个数。
这则说明了当环点数为 \(2\) 时,( 在前面,) 在后面一定不劣。
于是可以判掉点数为 \(2\) 的环,这样需要枚举的环点数肯定 \(\ge 4\),最坏时间复杂度 \(O(2^{\frac{n}{4}})\),能过 \(n=100\) 啦。

// lhzawa(https://www.cnblogs.com/lhzawa/)
// Problem: #6043. 「雅礼集训 2017 Day7」蛐蛐国的修墙方案
// Contest: LibreOJ
// URL: https://loj.ac/p/6043
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e2 + 10;
basic_string<int> h[N];
int p[N];
int vis[N];
int kh[N];
int main() {
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &p[i]);
	}
	int tot = 0;
	for (int i = 1; i <= n; i++) {
		if (! vis[i]) {
			tot++;
			for (int s = i; ! vis[s]; vis[s] = 1, h[tot] += s, s = p[s]);
			if (h[tot].size() == 2) {
				int a = h[tot][0], b = h[tot][1];
				kh[min(a, b)] = 1, kh[max(a, b)] = -1;
				h[tot].clear(), tot--;
			} 
		}
	}
	for (int k = 0; k < (1 << tot); k++) {
		for (int i = 1; i <= tot; i++) {
			int p = (k >> (i - 1)) & 1;
			for (int j = 0; j < (int)(h[i].size()); j++) {
				kh[h[i][j]] = (p ^ (j & 1)) ? 1 : -1; 
			}
		}
		int w = 0, Yes = 1;
		for (int i = 1; i <= n; i++) {
			w += kh[i];
			if (w < 0) {
				Yes = 0;
				break;
			}
		}
		if (Yes) {
			for (int i = 1; i <= n; i++) {
				printf("%c", kh[i] == 1 ? '(' : ')');
			}
			return 0;
		}
	}
	return 0;
}