洛谷 P4663 - [BalticOI 2008]魔法石(dp)
阿新 • • 發佈:2021-07-02
A:我該是有多無聊來寫這種題的題解啊
B:大概是因為這題題解區裡沒有題解所以我來寫一篇了,說明我有高尚的濟世情懷(大霧
跑題了跑題了
首先看到字典序第 \(i\) 小小可以自然地想到按位決策,也就是說從高位到低位列舉,對於每一位我們強制令它為 I
,看看有多少個符合要求的字串,如果小於 \(i\) 就將這位改為 \(1\) 並繼續往下列舉。
那麼怎麼求有多少個符合要求的字串呢?這時候就要用到 DP 了。顯然一個字串可以成為某個字串的正規讀法當且僅當它的字典序 \(\le\) 反串的字典序,因此我們很容易設計出一個 \(dp\) 狀態:\(dp_{i,j,o,x,y}\) 表示當前決策了字串的前 \(i\)
XI
相鄰,\(o\) 表示反串字典序是否等於原串字典序,從前往後數第 \(i\) 位為 \(x\),從後往前數第 \(i\) 位為 \(y\) 的方案數。轉移就列舉第 \(i+1\) 位填的數 \(u\) 和第 \(n-i\) 位填的數 \(v\),如果 \(o=1\) 且 \(u>v\) 就會導致原串字典序 \(>\) 反串字典序,不合法。否則顯然有如下轉移方程式子:
- \(dp_{i+1,j+[u\ne x]+[v\ne y],o\land[u==v],u,v}\leftarrow dp_{i,j,o,x,y}\)
注意特判 \(i+1=n-i\)
時間複雜度 \(32n^3\),跑得飛快(
const int MAXN=60; const char str[3]="IX"; int n,k;ll m,dp[MAXN+5][MAXN+5][2][2][2]; int lim[MAXN+5]; ll calc(){ memset(dp,0,sizeof(dp)); for(int a=0;a<2;a++) for(int b=a;b<2;b++){ if(~lim[1]&&(lim[1]^a)) continue; if(~lim[n]&&(lim[n]^b)) continue; dp[1][0][a==b][a][b]=1; } ll ans=0; for(int i=1;(i<<1|1)<=n;i++) for(int j=0;j<=k;j++) for(int o=0;o<2;o++) for(int x=0;x<2;x++) for(int y=0;y<2;y++){ if(!dp[i][j][o][x][y]) continue; if((i<<1|1)==n){ for(int u=0;u<2;u++){ if(~lim[i+1]&&(lim[i+1]^u)) continue; if(j+(u^x)+(u^y)<=k) ans+=dp[i][j][o][x][y]; } } else{ for(int u=0;u<2;u++) for(int v=0;v<2;v++){ if(~lim[i+1]&&(lim[i+1]^u)) continue; if(~lim[n-i]&&(lim[n-i]^v)) continue; if(o&&u>v) continue; if((i+1<<1)==n){ if(j+(u^x)+(u^v)+(v^y)<=k) ans+=dp[i][j][o][x][y]; } else dp[i+1][j+(u^x)+(v^y)][o&&u==v][u][v]+=dp[i][j][o][x][y]; } } } return ans; } int main(){ scanf("%d%d%lld",&n,&k,&m); if(n==1) return ((m>2)?puts("NO SUCH STONE"):(putchar(str[m-1]))),0; if(n==2){ if(k==0) return ((m>2)?puts("NO SUCH STONE"):(putchar(str[m-1]),putchar(str[m-1]))),0; else return ((m>3)?puts("NO SUCH STONE"):((m&1)?(putchar(str[m>>1]),putchar(str[m>>1])):puts("IX"))),0; } memset(lim,-1,sizeof(lim)); if(m>calc()) return puts("NO SUCH STONE")&0; for(int i=1;i<=n;i++){ lim[i]=0;ll sum=calc(); if(m>sum) m-=sum,lim[i]=1; } for(int i=1;i<=n;i++) putchar(str[lim[i]]); return 0; }