AtCoder3950 [AGC022E] Median Replace(DFA + dp)
problem
solution
可以從 DFA\text{DFA}DFA 的思想來考慮這道題。
考慮建一個 DFA\text{DFA}DFA 只接受最后可以變成字符串 111 的原串。
因為每次是選擇三個 連續/相鄰 的位置操作,限制是比較強的,無非有三種情況。
- case1 三個全 111,操作后只剩 111 個 111,相當于刪去兩個 111。
- case2 三個全 000,操作后只剩 111 個 000,相當于刪去兩個 000。
- case3 剩下的組合,操作后只剩 111 個 0/10/10/1,但都相當于刪去 010101 各一個。
顯然貪心地我們是不會操作 case1 減少 111 的數量的。而連續的 000 的數量也不會超過 222 個,超過一定可以操作回來。
只有 111 的數量大于等于 000 的數量就是合法的。
事實上最后情況 111 的數量絕對不可能跟 000 數量相同,因為題目保證輸入串是奇數長度,而每次的操作都減少兩個長度。
111 多了反正都會被接受,沒必要在 DFA\text{DFA}DFA 上建立這么多狀態。
我們將所有 111 的個數超過兩個的狀態都連回個數為 222 的狀態,然后接受這個狀態即可。
所以 DFA\text{DFA}DFA 的狀態數是 1(0/1/2)?0(0/1/2)=3?3=91(0/1/2)-0(0/1/2)=3·3=91(0/1/2)?0(0/1/2)=3?3=9 種的。
最后就是在這個 DFA\text{DFA}DFA 上跑 dpdpdp 即可。
設 f(i,j,k):f(i,j,k):f(i,j,k): 到字符串的第 iii 位,此時狀態為 jjj 個 111 和 kkk 個 000 的方案數??紤]向 i+1i+1i+1 位轉移。
- si+1=0s_{i+1}=0si+1?=0,直接增加 000 個個數即可。
- k=2k=2k=2,case2 操作。f(i,j,2)→f(i+1,j,0)f(i,j,2)\rightarrow f(i+1,j,0)f(i,j,2)→f(i+1,j,0)。
- k≠2k\neq 2k?=2,f(i,j,k)→f(i+1,j,k+1)f(i,j,k)\rightarrow f(i+1,j,k+1)f(i,j,k)→f(i+1,j,k+1)。
- si+1=1s_{i+1}=1si+1?=1。在這個時候才考慮跟 000 的抵消情況。
- k=0k=0k=0,f(i,j,0)→f(i+1,min?(j+1,2),0)f(i,j,0)\rightarrow f(i+1,\min(j+1,2),0)f(i,j,0)→f(i+1,min(j+1,2),0)。
- k≠0k\neq 0k?=0,case3 操作,f(i,j,k)→f(i,j,k?1)f(i,j,k)\rightarrow f(i,j,k-1)f(i,j,k)→f(i,j,k?1)。
- si+1=?s_{i+1}=?si+1?=?,將上面兩種情況都跑一遍即可。
code
#include <bits/stdc++.h> using namespace std; #define mod 1000000007 #define int long long #define maxn 300005 char s[maxn]; int n, ans; int f[maxn][3][3];signed main() {scanf( "%s", s + 1 );n = strlen( s + 1 );f[0][0][0] = 1;//f[i][j][k]:到字符串第i位時 有j個1 k個0for( int i = 0;i < n;i ++ ) {if( s[i + 1] != '0' ) {for( int j = 0;j <= 2;j ++ ) {( f[i + 1][j][0] += f[i][j][1] ) %= mod;( f[i + 1][j][1] += f[i][j][2] ) %= mod;( f[i + 1][min( j + 1, 2ll )][0] += f[i][j][0] ) %= mod;}}if( s[i + 1] != '1' ) {for( int j = 0;j <= 2;j ++ ) {( f[i + 1][j][1] += f[i][j][2] ) %= mod;( f[i + 1][j][2] += f[i][j][1] ) %= mod;( f[i + 1][j][1] += f[i][j][0] ) %= mod;}}}for( int i = 0;i <= 2;i ++ )for( int j = 0;j <= i;j ++ )( ans += f[n][i][j] ) %= mod; printf( "%lld\n", ans );return 0; }總結
以上是生活随笔為你收集整理的AtCoder3950 [AGC022E] Median Replace(DFA + dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux账号和权限管理 一看就会的那些
- 下一篇: 记github学生认证