\(\text{Description}\)

传送门

\(\text{Solution}\)

题目有一个限制:\(P_i\) 是个排列。

那么显然有:每个点只有一个点连向自己,只有一个点被自己连向。

是不是听起来很熟悉?这就是多个简单环组成的图(环大小可以为 \(1\))。

不过根据题目,发现出现奇环一定是无解的。因为要满足题目要求就必须在环上取一条边,这条边相邻的两条边都不能取,所以奇环是无解的。

只考虑偶环。对于每个偶环,显然就只有两种状态(环上相邻边取或不取序列):\(\{0,1,0,1\},\{1,0,1,0\}\)

不过这样是 \(\mathcal O(2^{\frac{n}{2}})\) 的,直接 \(\text T\) 飞。

不过我们发现长度为 \(2\) 的偶环是可以特判的!设这个偶环两点为 \(i,j\),且 \(i<j\),我们就令 \(i\) 为左括号,\(j\) 为右括号。因为这样不会增多未匹配的个数,反之就有可能。

你可能会觉得会不会有这种情况(好吧就是我觉得):\(j=i+1\),在 \(i\) 前正好有左括号,如果令 \(i\) 为右括号就将出现 \(()(\),反之为 \((()\)。你会发现如果第一种情况能构造出解右边就有右括号与 \(j\) 的左括号匹配,那么显然可以和前面的左括号匹配。

时间复杂度 \(\mathcal O(2^{\frac{n}{4}})\)

\(\text{Code}\)

#include <cstdio>

#define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
#define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
#define print(x,y) write(x),putchar(y)

template <class T> inline T read(const T sample) {
    T x=0; int f=1; char s;
    while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
    while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
    return x*f;
}
template <class T> inline void write(const T x) {
    if(x<0) return (void) (putchar('-'),write(-x));
    if(x>9) write(x/10);
    putchar(x%10^48);
}

#include <vector>
#include <cstdlib>
using namespace std;

const int maxn=105;

vector <int> g[maxn];
int n,p[maxn],cnt,siz[maxn];
bool vis[maxn],co[maxn];

void FindCircle(int u,int id) {
	if(vis[u]) return;
	g[id].push_back(u); vis[u]=1; ++siz[id];
	FindCircle(p[u],id);
}

void ok() {
	int tot=0;
	rep(i,1,n) {
		tot+=(co[i]?-1:1);
		if(tot<0) return;
	}
	rep(i,1,n) putchar(co[i]?')':'('); puts("");
	exit(0);
}

void dfs(int x) {
	if(x>cnt) return ok();
	if(siz[x]==2) {
		co[g[x][0]]=0,co[g[x][1]]=1;
		dfs(x+1);
		return;
	} 
	rep(i,0,g[x].size()-1) co[g[x][i]]=(i&1); dfs(x+1);
	rep(i,0,g[x].size()-1) co[g[x][i]]=(!(i&1)); dfs(x+1);
}

int main() {
	n=read(9);
	rep(i,1,n) p[i]=read(9);
	rep(i,1,n)
		if(!vis[i]) FindCircle(i,++cnt);
	dfs(1);
	return 0;
}
posted on 2020-12-24 21:36  Oxide  阅读(130)  评论(0)    收藏  举报