LOJ - 6043 「雅禮集訓 2017 Day7」蛐蛐國的修牆方案
阿新 • • 發佈:2020-12-24
\(\text{Description}\)
\(\text{Solution}\)
題目有一個限制:\(P_i\) 是個排列。
那麼顯然有:每個點只有一個點連向自己,只有一個點被自己連向。
是不是聽起來很熟悉?這就是多個簡單環組成的圖(環大小可以為 \(1\))。
不過根據題目,發現出現奇環一定是無解的。因為要滿足題目要求就必須在環上取一條邊,這條邊相鄰的兩條邊都不能取,所以奇環是無解的。
只考慮偶環。對於每個偶環,顯然就只有兩種狀態(環上相鄰邊取或不取序列):\(\{0,1,0,1\},\{1,0,1,0\}\)。
不過這樣是 \(\mathcal O(2^{\frac{n}{2}})\)
不過我們發現長度為 \(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; }