• 【BZOJ1141】[POI2009] Slw(神奇的斐波那契题目)


    点此看题面

    大致题意: 对于一个(01)(x),定义(H(x))为将(x)(1)替换为(10)(0)替换为(1)的结果,(H^m(x))则相当于对(x)(m)(H)操作后的最终串。现给定(n)以及(a_1,a_2,..,a_n),询问(H^{a_1}(0)H^{a_2}(0)...H^{a_n}(0))是否是某个(H^m(0))的子串((m)为任意自然数)。

    前言

    满脑子只有大模拟的我,在推出性质之后就一直在思考怎么合并。。。

    虽然和过去相比这次有些长进,一看模拟合并这么不可做,我也想过能不能用其他方法。结果想了半天也想不出还能怎么做,最后还是默默推合并至死。。。

    最后点开题解发现少推了一个性质,真的是菜。

    性质(1):斐波那契

    从这个东西是怎么联想到斐波那契的呢?

    我反正一看到(1)变成(10)(0)变成(1),就自然而然想到兔子繁殖,可以把(0)看成小兔子,(1)看成大兔子。

    于是我们就发现:(H^i(x)=H^{i-1}(x)+H^{i-2}(x)(ige 2))

    性质(2):开头与结尾

    开头:对于(H^i(x)),当(i)(0)时,开头是(0);当(i)(1)时,开头是(1)。根据斐波那契,每一个(H^i(x)(ige 2))的开头都是(H^{i-1}(x)),因此除(i=0)以外所有(H^i(x))开头必然是(1)

    结尾:根据斐波那契,我们会发现(H^i(x))的后半部分是(H^{i-2}(x)),也就是奇偶性相同的结尾相同。因此当(i)为偶数时(H^i(x))结尾是(0)(i)为奇数时(H^i(x))结尾是(1)

    性质(3):逆操作

    这是一个很关键的结论,就是因为没想到它,只推出前两个性质的我依旧啥也做不出来。

    若我们定义(x'=H^{-1}(x))表示(x=H(x'))(即(H^{-1}(x))(H(x))的逆操作),然后就会发现,若(x)是某一(01)(y)的子串,而根据先前斐波那契的性质(x')必然是(x)的子串,也就是(x')也是(y)的子串。

    实际上,我们不光可以从(x)(y)的子串推出(x')(y)的子串,还可以从(x')(y)的子串推出(x)(y)的子串(因为此处(H^m(x))我们完全可以视作(H^{infty}(x))),只不过在此题中这没啥用罢了。

    不合法情况

    现在,让我们来看看哪些情况是不合法的。

    1. 有连续两个(0)(良心的样例已经给了我们提示)。
    2. 考虑我们从已知不合法的两个(0)可以变换得到两个(1),但这貌似是合法的因为由(01)变换得到的(110)同样包含两个(1)。可如果是连续三个(1)呢?然后我们就发现这肯定是不合法的了。
    3. 再考虑我们从已知不合法的三个(1)可以变换得到(101010),这肯定也是不合法的。
    4. 。。。似乎根据我们的套路还可以继续从已知不合法的串往下推,但实际上已经没必要了。

    如何判断

    好了,综上所述讲了这么一大堆东西,那么我们究竟该怎么判断呢?

    考虑性质(3),我们可以在全部(a_i)都大于(0)时给它们全部减(1),直至减出(0)为止。

    然后我们要干什么呢?

    对于每一个(0)我们讨论它前面的数是什么,然后发现:

    • 如果前面是偶数,由于偶数结尾是(0),有连续两个(0),所以不合法。
    • 如果前面是奇数,进一步分类讨论:
      • 如果前面是(1)(H^1(0)=1)),那我们可以把(1)(0)合并为(2)
      • 如果前面是(3)(H^3(0)=101)),那我们可以把它们合并为两个(2)
      • 否则,由于(H^5(0)=10101),根据奇偶性相同的串结尾相同的结论,对于所有(ige 5)(i)是奇数的情况,(H^i(0))的结尾都是(10101),接上当前的(0)就是(101010),是上面的第三种不合法情况。

    简单的说,对于(1)我们得到一个(2),对于(3)我们得到两个(2),其他情况都不合法。

    最终如果能变成只剩一个数,那么就合法了。

    开头与结尾的特殊操作

    由于题目中要求的是子串,所以我们发现开头和结尾部分应该是可以向外扩展的,因此我们还需要进行如下几个操作:

    • 若开头是(0),此时由于前面没有数无法和上面一样分类讨论,考虑合法情况下向前扩展时(0)前面必然是(1)(因为两个(0)是非法的),所以我们可以把(0)修改为(2)
    • 若结尾是(1),显然它既可能是由(0)变来的,也可能是由(1)变来的(10)的前半部分,因此把它去掉完全不影响答案。如果结尾是(11),且不去掉这个(1),那么逆操作得到(00)就会判为非法。又如果结尾是(111),我们发现之前推不合法情况时之所以是三个(1),正是为了防止第二个(1)后面是(0),而我们去掉这第三个(1)后逆操作得到(00)正符合了我们的本意,那么此时第三个(1)也就没用了,完全可以去掉。
    • 若结尾是(3)(H^3(0)=101)),根据和上面相似的原因,我们可以去掉一个(1)得到(2)(H^2(0)=10)),这里就不再赘述了。

    这样一来,这道题就真正做完了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    using namespace std;
    int n,a[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    }F;
    I bool Check()//验证
    {
    	RI i,n_;W(n^1)
    	{
    		!a[1]&&(a[1]=2),a[n]==1&&--n,a[n]==3&&(a[n]=2);//特殊操作开头结尾
    		for(i=1;i<=n;++i) if(!a[i])//对于0
    			{if(a[i-1]==1) a[i-1]=2,a[i]=-1;else if(a[i-1]==3) a[i-1]=a[i]=2;else return 0;}//-1表示删除
    		for(n_=n,n=0,i=1;i<=n_;++i) ~a[i]&&(a[++n]=a[i]-1);//如果不是-1就统计下来
    	}return 1;
    }
    int main()
    {
    	RI Tt,i;F.read(Tt);W(Tt--) {for(F.read(n),i=1;i<=n;++i) F.read(a[i]);puts(Check()?"TAK":"NIE");}
    	return 0;
    }
    
  • 相关阅读:
    HTML5--Video
    线性表简述
    TortoiseSVN使用简介(转)
    JAVA EE(简述)
    WebService
    Java从零开始学四十七(注解简述)
    Java从零开始学四十六(Junit)
    二维数组---模拟斗地主
    Java从零开始学四十五(Socket编程基础)
    Java从零开始学四十四(多线程)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1141.html
Copyright © 2020-2023  润新知