括弧列でも良い気がするが。
https://yukicoder.me/problems/no/2133
問題
整数Nに対し、1~NのPermutation Pを考える。
加えて、Q個の条件が与えられる。
各条件は、数列Pにおける部分列P[L,R]を示す。
各部分列のprefixは、奇数の登場頻度が偶数の登場頻度以上でなければならず、かつ部分列全体で奇数と偶数が同数でなければならない。
条件を満たすPは何通りか。
解法
P中の奇数を"("、偶数を")"と置き換えると、各条件は部分列が正しい括弧列であることを意味する。
複数の条件があるが、互いに共通部分を持たないように分解しよう。そうすると、各条件における括弧列の組み合わせは、カタラン数で表現できる。
int N,Q; int C[2]; vector<int> E[505050]; const ll mo=998244353; const int NUM_=400001; static ll fact[NUM_+1],factr[NUM_+1],inv[NUM_+1]; ll catalan(int n) { return fact[2*n]*factr[n]%mo*factr[n+1]%mo; } ll ret=1; void dfs(int L,int R) { if((R-L)%2) { cout<<0<<endl; exit(0); } int num=1; L++; while(L<R) { if(E[L].size()) { dfs(L,E[L][0]); L=E[L][0]; } else { num++; L++; } } ret=ret*catalan(num/2)%mo; } void solve() { int i,j,k,l,r,x,y; string s; inv[1]=fact[0]=factr[0]=1; for (int i=2;i<=NUM_;++i) inv[i] = inv[mo % i] * (mo - mo / i) % mo; for (int i=1;i<=NUM_;++i) fact[i]=fact[i-1]*i%mo, factr[i]=factr[i-1]*inv[i]%mo; cin>>N>>Q; C[0]=N/2; C[1]=N-N/2; if(N%2) N++; E[0].push_back(N); FOR(i,Q) { int L,R; cin>>L>>R; E[L-1].push_back(R); } FOR(i,N) if(E[i].size()) { sort(ALL(E[i])); E[i].erase(unique(ALL(E[i])),E[i].end()); if(E[i][0]==i) E[i].erase(E[i].begin()); if(E[i].empty()) continue; FOR(j,E[i].size()-1) { E[E[i][j]].push_back(E[i][j+1]); } E[i].resize(1); for(j=i+1;j<E[i][0];j++) { int f=0; FORR(e,E[j]) { if(e>=E[i][0]) f=1; } if(f) { E[j].push_back(E[i][0]); E[i][0]=j; break; } } } ret=1; dfs(0,N); FOR(i,C[0]) ret=ret*(i+1)%mo; FOR(i,C[1]) ret=ret*(i+1)%mo; cout<<ret<<endl; }
まとめ
複数クエリが直行するように分解するの、時々出てくるな。
なんかライブラリ化した方がいいのかな。