【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)(i\ge 2)\)。
性质\(2\):开头与结尾
开头:对于\(H^i(x)\),当\(i\)为\(0\)时,开头是\(0\);当\(i\)为\(1\)时,开头是\(1\)。根据斐波那契,每一个\(H^i(x)(i\ge 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\),根据奇偶性相同的串结尾相同的结论,对于所有\(i\ge 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;
}