BZOJ1141: [POI2009]Slw

77 篇文章 0 订阅

好神呀…

手玩一下发现这个H(s)生成的串有个斐波那契的递推式
emmmmm然后我就啥都不会了….

我们定义G(s)为H(s)的逆变换,即H(a)=b,G(b)=a
那么对于s中的每个1,如果后面是0,他会变成1,否则变成0
发现我们要验证s是否是S的子串,这个问题等价于验证G(s)是否是G(S)的子串
我们可以不断对s做逆变换,让他越来越短

我们先来证几个结论
1: Hn H n 的偶数项最后一位是0,奇数项最后一位是1
2: Hn(n>0) H n ( n > 0 ) 第一位为1
3: Hn H n 中不存在00
4: Hn(nn>=5) H n ( n 为 奇 数 且 n >= 5 ) 最后有一段10101
5: Hn(nn>=5) H n ( n 为 奇 数 且 n >= 5 ) 后面不能接0 (由性质4,若接0,101010,对 Hn H n 逆变换2次后会有00项,一定不是S的子串(由性质2))

用发现的那个斐波那契性质易证

然后考虑对s做逆变换

s由很多个 Hai H a i 拼在一起,当ai都>0时, Hai(i<n) H a i ( i < n ) 后面都跟着一个1(结论2),所以不用考虑后面的东西对 Hai H a i 逆变换的影响, G(Hai)=Hai1 G ( H a i ) = H a i − 1 直接ai-=1

对于 Han H a n
若an为偶数,s最后一位时0,s后面的东西不会影响G(s),直接an–,
若an为奇数,当an=1时,因为s后面可以跟1或0,发现此时an不影响整个s是否是S的子串,直接将an删去;an=3时,同样可以删去最后一个1,让an=2,an-=1变成1;an>=5时,因为后面不能接0,可以直接an-=1

一直这样缩直到出现 ai=0 a i = 0 ,这时不能再分别缩了因为没有 G(H0) G ( H 0 ) 这个东西,要将他和前面合并
对于一个位置 ai=0 a i = 0
ai1 a i − 1 是偶数,出现00不合法
ai1 a i − 1 是奇数
1: ai1=1 a i − 1 = 1 ,合并在一起, ai1=2 a i − 1 = 2
2: ai1=3 a i − 1 = 3 ,变成1010, ai1=ai=2 a i − 1 = a i = 2
3: ai1>=5 a i − 1 >= 5 ,出现101010,不合法

因为 ni=1ai<=1e7 ∑ i = 1 n a i <= 1 e 7 操作直接暴力过整个序列就行了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
const int maxn = 1100000;

int n,m;
int a[maxn];

int main()
{
    int tcase; read(tcase);
    while(tcase--)
    {
        read(n);
        for(int i=1;i<=n;i++) read(a[i]);

        bool flag=true;
        while(n>1&&flag)
        {
            if(a[n]==1) n--;
            else if(a[n]==3) a[n]=2;
            for(int i=1;i<=n;i++) if(!a[i])
            {
                if(i==1) a[i]=2;
                else
                {
                    int j=i-1; if(!a[j]) j--;
                    if(a[j]&1)
                    {
                        if(a[j]>=5) { flag=false; break; }
                        else if(a[j]==1) a[j]=2;
                        else a[j]=a[i]=2;
                    }
                    else { flag=false; break; }
                }
            }
            m=0; for(int i=1;i<=n;i++) if(a[i]) a[++m]=a[i]-1;
            n=m;
        }
        puts(flag?"TAK":"NIE");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值