TJOI2018 遊園會 狀壓dp LCS

2022-03-29 05:37:18 字數 1959 閱讀 7735

小豆參加了 noi 的遊園會,會場上每完成乙個專案就會獲得乙個獎章,獎章只會是 n, o, i 的字樣。

在會場上他收集到了k個獎章組成的串。兌獎規則是獎章串和兌獎串的最長公共子串行長度為小豆最後獎勵的等級。

現在已知兌獎串長度為 n ,並且在兌獎串上不會出現連續三個獎章為noi,即獎章中不會出現子串noi

現在小豆想知道各個獎勵等級會對應多少個不同的合法兌獎串。

宣告:為了避免變數重複,我們將題麵中的k用m代替。記獎章串為a串,兌獎串為b串.noi三個字元分別用數字0,1,2代替

容易想到計數dp,設\(dp[i][j]\)表示長度為\(i\),與\(b\)串的lcs為\(j\)的合法a串數量。但是只有lcs長度我們無法轉移。考慮求lcs的過程

\(lcs[i][j]=\max(lcs[i][j-1],lcs[i-1][j],(lcs[i-1][j-1]+1)[a_i=b_j])\)

由於b串是固定的,那麼a串第i個位置的字元確定了,轉移後\(lcs[i]\)也確定了。而根據\(lcs[i]\),我們可以求出當前的lcs長度\(\max_(lcs[i][j])\).那麼,我們就可以用\(lcs[i]\)來作為計數dp的子狀態。而容易發現\(lcs[i]\)是單調遞增的乙個序列,且元素之間最多相差1.於是我們不用記錄\(lcs[i]\),而是記錄\(lcs[i]\)的差分.把每個數加起來就得到了真正的lcs.因為\(m \leq 15\),所以可以狀壓。

根據前面的討論,我們可以在dp之前預處理出\(sta[s][c]\)表示s狀態的lcs,加上確定的字元c轉移後的狀態。容易發現s的二進位制裡1的個數就是lcs長度。

注意到還有不能出現noi的限制,再記錄一維表示當前串末尾與noi匹配的位數。

綜上,子狀態\(dp[i][s][j]\)表示長度為\(i\),\(lcs[i]\)的差分為\(s\), 末尾與noi匹配的位數為\(j\)的a串個數。

那麼我們可以寫出轉移方程:

\(dp[i+1][sta[s][k]][match[j][k]]+=dp[i][s][j] (k\in \,j和k不同時為2)\)

其中\(match[j][k]\)表示末尾與noi匹配的位數為\(j\)時,加上k字元後的匹配位數。直接手玩一下再打個表就可以了。實現上注意第一位要滾動陣列。

時間複雜度\(o(n 2^m)\)

#include#include#include#define mod 1000000007

#define maxn 1000

#define maxm 15

#define maxs 32768

using namespace std;

typedef long long ll;

int n,m;

const char noi="noi";

char str[maxn+5];

int b[maxn+5];

int sta[maxs+5][3];

void ini()

int t=0;

for(int j=1; j<=m; j++)

sta[s][k]=t;//當a[i]=k時,dp狀態會由s變成t

// printf("trans[%d][%d]=%d\n",s,k,sta[s][k]);

} }}const int match[3][3]=,,};

//match[i][j]表示原本末尾與匹配了i位,加上字元noi[j]之後末尾匹配的位數

inline int count_1(int x)

return ans;

}ll ans[maxm+5];

ll dp[2][maxs+5][3];

int main()

}} now^=1;

} for(int s=0;s<(1<} for(int i=0;i<=m;i++) printf("%lld\n",ans[i]);

}

繼續遊園會

今天換個公園 也換個主題,去體育公園看豐富的體育運動.請注意,是看體育,本人到目前為止尚無法做到享受體育.不管怎樣,有意識就是值得鼓勵的,自己先給自己鼓掌了 早上我們都沒有睡懶覺,和寶寶飛奔到公園,今天好好地耍它一上午.那麼多的活動,寶寶卻似乎只對她的泡泡槍感興趣,打出一串串晶瑩剔透的泡泡吸引著和她...

TJOI2018 數學計算

如果採取暴力的做法,那麼乘起來會炸longlong,除非寫個高精。再考慮乘一下逆元呢,顯然也不行,模數不一定為質數。這道題的關鍵點在於這句話,對於每乙個型別1的操作至多會被除一次 這句話的最基本的告訴了我們每次得到的答案一定是乙個整數 其次,這句話保證了可以應用線段樹解決這個問題 如果除的操作可能會...

Tjoi2018 數學計算

bzoj luogu 線段樹分治 是不是想問為什麼不暴力做?模數沒說是質數,所以不一定有逆元.然後就是要每次build一下把線段樹權值init成1,博豬不知道為什麼for就wa,build就過了 用re自動機查了下,發現還是有0.for int i 1 i 1 i s i 1 define ls x...