1. 程式人生 > 實用技巧 >P5362 [SDOI2019]連續子序列 思維題

P5362 [SDOI2019]連續子序列 思維題

題意:

戳這裡

分析:

不會分析 , 直接找規律:

肉眼分析可以發現, \(T.M\) 序列的生成方式有兩種:

  1. 取反然後複製一遍 \(0110\to 01101001\)
  2. \(\tiny \color{white}{\text{不}}\)容易發現 \(0110\to 0(1)1(0)1(0)0(1)\)

但是第一個性質不方便用,因為 \(|S|+k\) 不一定是 \(2\) 的整數次冪,所以我們考慮第二種方法

我們發現在後面新增的 \(k\) 的布林序列也會是 \(01\) ,\(10\) 交替的,所以一定不會存在連續的三個以上的相同字元,所以我們可以反向推導,由第二個性質可以知道 \(0\to 01\) \(1\to 10\)

所以我們每一次可以將 \(S\) 長度減半 \(k\) 也減半,這樣遞迴 \(\log k\) 層就能得到 \(k<=2\) 的情況,然後我們手動判掉 \(n,k<=3\) 的情況,順便對 \(S,k\) 雜湊一下之後記憶化

具體來說就是,每次分兩種情況討論,一種是第一位空出來與前面的拼成一個 \(0\),第二種就是從第一位開始兩個一組

程式碼:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define psl pair<string,long long>

using namespace std;

namespace zzc
{
    typedef long long ll;
    const ll mod = 1e9+9;
    map<psl,ll> f;
    
    long long solve(string s,long long k)
    {
       if(f[mk(s,k)]) return f[mk(s,k)];
       long long n=s.size(),res=0;
       if(n==1&&k<=2) return k+1;
       if(n==2&&k==0) return 1;
       if(n==2&&k==1) return (s[0]==s[1])?1:2;
       if(n==3&&k==0) return (s[0]!=s[1]||s[1]!=s[2]);
       bool flag=true;
       string nxt;
       for(int i=0;i<n;i+=2)
       {
           if(i==n-1||s[i]!=s[i+1]) nxt+=s[i];
           else {flag=false;break;}
       }
       if(flag) res+=solve(nxt,(n&1)?(k>>1):((k+1)>>1))%mod;
       nxt=((s[0]-'0')^1)+'0';
       flag=true;
       for(int i=1;i<n;i+=2)
       {
           if(i==n-1||s[i]!=s[i+1]) nxt+=s[i];
           else {flag=false;break;}
       }
       if(flag) res+=solve(nxt,(n&1)?((k+1)>>1):(k>>1))%mod;
       return f[mk(s,k)]=res%mod;
    }

	void work()
	{
        ios::sync_with_stdio(false);
        string s;
        ll k,t;
        cin>>t;
        while(t--)
        {
            cin>>s>>k;
            cout<<solve(s,k)%mod<<'\n';
        }
	
	}

}

int main()
{
	zzc::work();
	return 0;
}