[luogu5362] [SDOI2019R2 d2t3] 連續子序列 - 字符串 - 找規律 - 結論題 - 記憶化搜索

傳送門:https://www.luogu.org/problemnew/show/P5362

題目大意:有一個01串tm,滿足tm(0)=0,tm(i)=tm(i>>1)^(i&1)。給定01串s和整數k,問有多少個本質不同的01串t滿足:t是tm的子串,s是t的前綴,t的長度是|s|+k。

全場無人ac的找規律神題。

關於tm序列,有很多種解釋方式,比如每個數的二進制中1的個數的奇偶性啦,從0開始每次把自身取反接在後面啦等等。對於這個題,最好的解釋方式是這樣的:

從0開始,每次把所有的0變成01,把所有的1變成10。

爲什麼這樣解釋最好呢?因爲我們可以試着把一個tm的子串用這種方式變換回去:

假設一個串s=abcdefg

我們可以把它每2位劃分爲一段,即ab|cd|ef|g或a|bc|de|fg,然後把每一段按上述規則合併爲一個字符。

如果某一段內兩個字符相同,意味着這樣劃分是不合法的。

兩側也許有落單的段,但是根據規則實際上與它們成一段的另一個字符是確定的,我們也可以將其補上。

比如串10100110010,就可以切割爲10|10|01|10|01|0,然後合併成110100。

可以驗證:當串長>3時,合法的切割方案是唯一的。比如串1001,就只能劃分爲10|01,而串1010看似可以劃分爲1|01|0,但是由於這樣縮成的是000因此不合法,所以唯一的劃分方案是10|10。

因此我們就可以通過對切割方案的計數來實現對串的計數。設f(s,k)表示當前串爲s,後面接k個字符的方案數。當|s|+k<=3時特判,否則枚舉2種劃分方案,對於合法的方案進行遞歸操作。用個map對狀態進行記憶化即可。

本質不同的狀態數大約只有O(log|s|+logk)級別,可以通過此題。

ps:關於tm序列的一些其他的性質,可以參見http://oeis.org/A010060http://www.matrix67.com/blog/archives/5822

#include<bits/stdc++.h>
using namespace std;
#define gc getchar()
#define pc putchar
#define li long long
int t;
string s;
li k;
#define psi pair<string,li>
#define fi first
#define se second
#define mp make_pair
map<psi,li> mm;
const int mo = 1000000009;
inline li wk(psi p){
    if(p.fi.size() == 1){
        if(!p.se) return 1;
        if(p.se == 1) return 2; 
        if(p.se == 2) return 3;
    }
    if(p.fi.size() == 2){
        if(!p.se) return 1;
        if(p.se == 1) return (p.fi[0] == p.fi[1] ? 1 : 2);
    } 
    if(p.fi.size() == 3 && !p.se) return (p.fi[0] == p.fi[1] && p.fi[1] == p.fi[2] ? 0 : 1);
    if(mm.find(p) != mm.end()) return mm[p];
    li as = 0;
    int i;
    bool fg = 1;
    string nxt;
    nxt.clear();
    for(i = 0;i < p.fi.size();i += 2){
        if(i == p.fi.size() - 1) nxt += p.fi[i];
        else if(p.fi[i] == p.fi[i + 1]){
            fg = 0;break;
        }
        else nxt += p.fi[i];
    } 
    if(fg) as += wk(mp(nxt,(p.fi.size() % 2 ? (p.se >> 1) : (p.se + 1 >> 1))));
    fg = 1;nxt.clear();
    nxt += (p.fi[0] ^ 1);
    for(i = 1;i < p.fi.size();i += 2){
        if(i == p.fi.size() - 1) nxt += p.fi[i];
        else if(p.fi[i] == p.fi[i + 1]){
            fg = 0;break;
        }
        else nxt += p.fi[i];
    } 
    if(fg) as += wk(mp(nxt,(p.fi.size() % 2 ? (p.se + 1 >> 1) : (p.se >> 1))));
    return mm[p] = as;
}
int main(){
    cin>>t;
    while(t--){
        cin>>s>>k;
        cout<<wk(mp(s,k)) % mo<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章