• [SDOI2019] 连续子序列


    题意:

    我们定义TM序列为如下形式的布尔序列:

    • $T_0 = 0$;
    • $T_{2n}=T_n$;
    • $T_{2n+1}=1-T_n$。

    TM序列是一个无限长度的序列,它有很多连续子序列。

    现在给定一个布尔序列S和一个非负整数k,请统计一下一共有多少种TM序列的连续子序列T满足:

    • S是T的前缀;
    • T是由S额外在右侧添加了恰好k项形成的。

    $数据组数Tleq 100,|S|leq 100,kleq 10^{18}$。

    题解:

    神仙诈骗题。

    该序列的生成方式有无数种,我一开始想的是每次取反拼在后面,但好像没什么操作空间。

    注意到k的范围是$10^{18}$,这个数据范围要么是$O(1)$要么是$O(log{k})$。

    发现自己不会$O(1)$,于是考虑$O(log{k})$,也就是每次把长度压缩成原来的一半。

    考虑该序列的另外一个生成方式:每次将0替换成01,将1替换成10。我们反着做这个操作即可压缩序列。

    虽然这k个空填啥都行,但仔细想一下,缩的过程中它们都会跟某个确定的元素配对一次,实际上没有累加贡献。

    注意每次缩的时候都有两种情况:把第一个元素跟前面/后面配对,在$nleq 3$的时候还得特判一下。

    复杂度$O(log{k})$,因为每个串最后只能缩成一个串。

    套路:

    • 没思路$ ightarrow$根据数据范围猜做法。
    • 想了一会还是没思路$ ightarrow$直接写个优秀的暴力走人。

    代码:

    #include<bits/stdc++.h>
    #define maxn 100005
    #define maxm 500005
    #define inf 0x7fffffff
    #define mod 1000000009
    #define ll long long
    #define mp make_pair
    #define rint register int
    #define debug(x) cerr<<#x<<": "<<x<<endl
    #define fgx cerr<<"--------------"<<endl
    #define dgx cerr<<"=============="<<endl
    
    using namespace std;
    map<pair<string,ll>,ll> dp;
    string S;
    
    inline ll read(){
        ll x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    inline ll solve(string s,ll k){
        if(dp[mp(s,k)]) return dp[mp(s,k)];
        ll n=s.length(),res=0;
        if(n==1 && k<=2) return dp[mp(s,k)]=k+1;
        if(n==2 && k==0) return dp[mp(s,k)]=1;
        if(n==2 && k==1) return dp[mp(s,k)]=(s[0]==s[1])?1:2;
        if(n==3 && k==0) return dp[mp(s,k)]=(s[0]!=s[1]||s[1]!=s[2]); 
        string ns; bool flag=1;
        for(rint i=0;i<n;i+=2){
            if(i==n-1 || s[i]!=s[i+1]) ns+=s[i];
            else{flag=0;break;}
        }
        if(flag) res=(res+solve(ns,(n&1)?(k>>1):((k+1)>>1)))%mod;
        flag=1,ns=((s[0]-'0')^1)+'0';
        for(rint i=1;i<n;i+=2){
            if(i==n-1 || s[i]!=s[i+1]) ns+=s[i];
            else{flag=0;break;}
        }
        if(flag) res=(res+solve(ns,(n&1)?((k+1)>>1):(k>>1)))%mod;
        return dp[mp(s,k)]=res;
    }
    
    int main(){
        ll T=read();
        while(T--){
            dp.clear();
            cin>>S; ll k=read();
            printf("%d
    ",solve(S,k));
        }
        return 0;
    }
    连续子序列
  • 相关阅读:
    js正则表达式基本语法
    类似于QQ的右滑删除效果的实现方法
    JS设置cookie、读取cookie、删除cookie
    JavaScript随机生成颜色的方法
    mysql数据库备份及恢复
    Javascript 实现简单计算器实例代码
    JavaScript 实现的checkbox经典实例分享
    网页瀑布流布局jQuery实现代码
    Django Web在Apache上的部署
    VIM使用系列之一——配置VIM下C/C++编程环境
  • 原文地址:https://www.cnblogs.com/YSFAC/p/13112960.html
Copyright © 2020-2023  润新知