[JZOJ5029]围墙

来源:互联网 发布:电商美工是干什么的 编辑:程序博客网 时间:2024/04/28 03:59

题目大意

给定一个长度为n的排列{Pi}(i,Pii),要求构造一种合法的括号序列,满足:
构造一个n个点的图,当第i个位置是左括号时,存在边(i,Pi),最后形成的图必须满足每个点度数为1
输出任意一种方案即可,数据保证有解。

1n100


题目分析

显然我们先将所有(i,Pi)连上会形成若干个互不连通的简单环,一个简单环只要确定了一个点是左还是右就能确定整个环的状态。那么我们显然可以搜索加判断,因为每个环最小大小是2,所以复杂度是O(2n/2)
考虑怎么优化,可以发现大小为2的环其实可以直接根据先后顺序确定填什么。那么我们直接特判掉大小为2的环,剩下的环最小大小是4(奇数显然无解),于是复杂度降为O(2n/4)。是不是很机智?


代码实现

#include <algorithm>#include <iostream>#include <cstdio>using namespace std;const int N=105;int P[N],fa[N],rank[N];bool seq[N],cet[N];int n;int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}inline int merge(int x,int y){    if (rank[x]>rank[y]) swap(x,y);    fa[x]=y,rank[y]+=rank[x]==rank[y];}inline void go(int x){for (int y=P[x],c=seq[x];x!=y;c=seq[y]=c^1,cet[y]^=1,y=P[y]);}bool dfs(int x,int sum){    if (sum<0) return 0;    if (x==n+1) return !sum;    if (!cet[x])    {        /*if (seq[x]=0,go(x),dfs(x+1,sum+(seq[x]?-1:1))) return 1;        go(x);        if (P[P[x]]==x) return 0;        if (seq[x]=1,go(x),dfs(x+1,sum+(seq[x]?-1:1))) return 1;        return go(x),0;*/        for (int c=0,y,z,cnt;c<2;++c)        {            for (cnt=1,z=seq[x]=c,y=P[x];x!=y;z=seq[y]=z^1,cet[y]=1,y=P[y],++cnt);            if (dfs(x+1,sum+(seq[x]?-1:1))) return 1;            if (cnt==2) return 0;        }        for (int y=P[x];x!=y;y=P[y],cet[y]=0);        return 0;    }else return dfs(x+1,sum+(seq[x]?-1:1));}int main(){    freopen("wall.in","r",stdin),freopen("wall.out","w",stdout);    scanf("%d",&n);    for (int i=1;i<=n;++i) fa[i]=i;    for (int i=1;i<=n;++i)    {        scanf("%d",&P[i]);        int fx=getfather(i),fy=getfather(P[i]);        if (fx!=fy) merge(fx,fy);    }    dfs(1,0);    for (int i=1;i<=n;++i) putchar(seq[i]?')':'(');    printf("\n");    fclose(stdin),fclose(stdout);    return 0;}
0 0