21.4.21 t1
阿新 • • 發佈:2021-06-26
tag:構造
題意
設計一個確定性有限狀態自動機,使得恰好能接受1~n的全排列中的 \(q\) 個
\(n\leq12,0\leq q\leq n!\)
輸出
第一行為狀態數 \(Q(Q\le n+1)\)
接下來 \(Q\) 行,每行 \(n\) 個數。第 \(i\) 行第 \(j\) 個數 \(a_{i,j}\) 表示第 \(i\) 個狀態在遇到 \(j\) 時會轉移到狀態 \(a_{i,j}\)
接下來一行一個數 \(q_0\) 表示初始狀態為 \(q_0\)
接下來一行若干個數,第一個數為接受的狀態數 \(n\),後面 \(n\) 個不同整數\(f_1,f_2\cdots f_n\) 表示接受狀態 \(f_1,f_2\cdots f_n\)
實際上可以構造出自動機恰好接受從 \(1,2\cdots n\) 一直到第 \(q\) 個排列。
將第 \(n\) 個狀態當作“垃圾桶”,第 \(n+1\) 個狀態當作"終點"。這兩個狀態都是自己連自己。
舉個例子就很明顯了。\(n=4\),接受所有小於 \(3,2,4,1\) 的排列。
拆分一下:
- 接受所有以 \((1)\)、\((2)\) 開頭的排列
連 \((1\to n+1,1/2),\ (1\to2,3)\ (1\to n,4)\)
- 接受所有以 \((3,1)\) 開頭的排列
連 \((2\to n+1,1),\ (2\to3,2)\ (2\to,n,3/4)\)
- 接受所有以 \((3,2,1)\)
連 \((3\to n+1,1/4),\ (3\to n, 2/3)\)
思路就是逐位限定。當前狀態向終點 \(n+1\) 的連邊就是當前位接受的狀態,然後再向下一個狀態連一條邊,其餘的邊都往垃圾桶 \(n\) 連。
#include<bits/stdc++.h> using namespace std; template<typename T> inline void Read(T &n){ char ch; bool flag=false; while(!isdigit(ch=getchar()))if(ch=='-')flag=true; for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48)); if(flag)n=-n; } int n, q; char vis[15]; int ans[15][15]; int getnxt(int x){x++;while(vis[x])x++;return x;} int main(){ // freopen("1.out","w",stdout); Read(n); Read(q); int tp = 1; vis[0] = true; for(register int i=1; i<n; i++) tp *= i; printf("%d\n",n+1); for(register int i=1; i<n; i++){ int k = q/tp, x=0; q %= tp; while(k--) x=getnxt(x), ans[i][x] = n+1; vis[x=getnxt(x)] = true; ans[i][x] = i+1; tp /= (n-i); } for(register int i=1; i<=n; i++,puts("")) for(register int j=1; j<=n; j++) printf("%d ",ans[i][j]?ans[i][j]:n); for(register int i=1; i<=n; i++) printf("%d ",n+1);puts(""); puts("1"); printf("1 %d\n",n+1); return 0; }