zoukankan      html  css  js  c++  java
  • 并不对劲的loj3115:p5362:[SDOI2019]连续子序列

    题目大意

    有一个无限长的01串(T)满足:
    n=0时,(T_n=0);n为偶数时,(T_n=T_{frac{n}{2}});n为奇数时,(T_n=1-T_{frac{n-1}{2}})
    该串的前几位:01101001100101101001011001101001
    多组询问,每次询问给定01串(S)和长度(k),问对于所有在(T)里出现的和(S)一样的子串,它们后面紧接的(k)位有多少种不同的01串。
    (询问数leq 100;|S|leq 100;kleq 10^{18};)

    题解

    T这个01串相当于是:
    一开串有个0,然后把0换为01变成01,把0换为01把1换为10变成0110,同样的变换变为01101001,变为0110100110010110。
    这样转化有个好处就是可以反着来,可以考虑把(S)串长度减半。
    可以把(S)串每相邻两个分一组,按10->1,01->0的规则逆变换;或者在(S)最前面在加一个0或1,在按这个规则逆变换。
    发现当分到同一组的存在00或11时,变换就是不合法的。
    对于任何一个长度大于等于6的串,它其中只要出现连续三次01交替的,逆变换一次就会出现000或111,怎么分都不合法;如果没有出现连续三次01交替,就一定有1001或0110,它们都只有1中逆变换方法。
    对于长度为4或5的串,如果有连续两次01交替,逆变换一次后出现00或11,00或11只有一种逆变换方法;如果没有,同上。
    所以长度大于3的串的合法的逆变换方法不超过1中。可以只对长度不超过3的串搜索,并手算出长度为1或2的串的一部分比较好算的答案。
    在对(S)进行逆变换时,同时也要对后(k)位逆变换:如果(S[|S]-1])没和(S[|S|])分到一组,为了使逆变换合法,相当于是(S)后的第一位已经确定了,所以(k)的长度逆变换后是(lfloorfrac{k}{2} floor);反之,长度为(k)的串能分(lceilfrac{k}{2} ceil)组,(k)逆变换后是(lceilfrac{k}{2} ceil)
    在经过几次逆变换后(S)的长度会变成1,但因为(k)极大,可能还会再变换几次才变成手算出的那部分的大小。这样时间看上去是(2^{log_2space 10^{18}}=10^{18})级别的。
    但是发现(k)递归时会变成(lfloorfrac{k}{2} floor)(lceilfrac{k}{2} ceil),也就是说搜到的不同的(k)的值数量是在(log_2space k)级别的。可以记忆化搜索。

    代码
    #include<algorithm>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<ctime>
    #include<iomanip>
    #include<iostream>
    #include<map>
    #include<queue>
    #include<set>
    #include<stack>
    #include<vector>
    #define rep(i,x,y) for(register int i=(x);i<=(y);++i)
    #define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
    #define psl pair<string,LL>
    #define LL long long
    #define fi first
    #define se second
    #define mp make_pair
    using namespace std;
    LL read()
    {
    	LL x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)&&ch!='-')ch=getchar();
    	if(ch=='-')f=-1,ch=getchar();
    	while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    	return x*f;
    }
    void write(int x)
    {
    	if(x==0){putchar('0'),putchar('
    ');return;}
    	int f=0;char ch[20];
    	if(x<0)putchar('-'),x=-x;
    	while(x)ch[++f]=x%10+'0',x/=10;
    	while(f)putchar(ch[f--]);
    	putchar('
    ');
    	return;
    }
    const int mod=1e9+9;
    int t;
    LL n;
    string s,nxts;
    map<psl,int>M;
    int mo(int x){x%=mod;if(!x)x=mod;return x;} 
    int work(psl x)
    {
    	//cout<<x.fi<<" "<<x.se<<endl;
    	if(x.fi.size()==1){if(x.se<=2)return x.se+1;}
    	if(x.fi.size()==2)
    	{
    		if(x.se==0)return 1;
    		if(x.se==1)return (x.fi[0]==x.fi[1])?1:2;
    	}
    	if(x.fi.size()==3)
    	{
    		if(x.fi[0]==x.fi[1]&&x.fi[1]==x.fi[2])return 0;
    		if(x.se==0)return 1;
    	}
    	if(M[x]){return M[x];}
    	nxts.clear();int li=x.fi.size()-1,no=0,res=0;
    	for(int i=0;i<=li;i+=2)
    	{
    		if(i+1<=li&&x.fi[i]==x.fi[i+1]){no=1;break;}
    		else nxts+=x.fi[i];
    	}
    	if(!no){res=work(mp(nxts,(x.fi.size()&1)?(x.se>>1ll):(x.se+1ll>>1ll)));}
    	nxts.clear();no=0;
    	nxts+=(x.fi[0]=='1')?'0':'1';
    	for(int i=1;i<=li;i+=2)
    	{
    		if(i+1<=li&&x.fi[i]==x.fi[i+1]){no=1;break;}
    		else nxts+=x.fi[i];
    	}
    	if(!no){res+=work(mp(nxts,(x.fi.size()&1)?(x.se+1ll>>1ll):(x.se>>1ll)));}
    	return M[x]=mo(res);
    }
    int main()
    {
    	t=read();
    	while(t--)
    	{
    		cin>>s;n=read();
    		write(work(mp(s,n))%mod);
    	}
    	return 0;
    }
    
    奇怪的思路

    (T_i=(i的二进制数位和)modspace 2) 。然而毫无用处。

  • 相关阅读:
    bootstrap以及考试复习
    HTML复习
    驼峰命名法和模态对话框
    dom和bom
    dom习题复习和讲解
    DOM
    属性扩展
    sql防注入式攻击
    自动生成编号
    删除,修改,添加
  • 原文地址:https://www.cnblogs.com/xzyf/p/13034132.html
Copyright © 2011-2022 走看看