大致题意: 对于一个(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))),只不过在此题中这没啥用罢了。
不合法情况
现在,让我们来看看哪些情况是不合法的。
- 有连续两个(0)(良心的样例已经给了我们提示)。
- 考虑我们从已知不合法的两个(0)可以变换得到两个(1),但这貌似是合法的因为由(01)变换得到的(110)同样包含两个(1)。可如果是连续三个(1)呢?然后我们就发现这肯定是不合法的了。
- 再考虑我们从已知不合法的三个(1)可以变换得到(101010),这肯定也是不合法的。
- 。。。
似乎根据我们的套路还可以继续从已知不合法的串往下推,但实际上已经没必要了。
如何判断
好了,综上所述讲了这么一大堆东西,那么我们究竟该怎么判断呢?
考虑性质(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;
}