P1030 · OFFICIAL SOLUTION

P1030 鹧鸪天 官方题解

Gioush OJ · P1030 鹧鸪天

鹧鸪天

【题意简述】

设第 ii 盏灯的状态为 bi{0,1}b_i\in\{0,1\},并令边界外的 b0=bn+1=0b_0=b_{n+1}=0。给定

ai=bi1+bi+bi+1,a_i=b_{i-1}+b_i+b_{i+1},

求共有多少个状态序列满足全部条件。

【Hint】

只需要枚举第一盏灯的状态,之后每个位置都会被上一条等式唯一推出。

【提示】

固定 b0=0b_0=0 并枚举 b1b_1 后,由 ai1=bi2+bi1+bia_{i-1}=b_{i-2}+b_{i-1}+b_i 可以依次推出 bib_i

【解法】

b0=bn+1=0b_0=b_{n+1}=0,枚举 b1{0,1}b_1\in\{0,1\}。对 i=2,3,,ni=2,3,\dots,n,递推

bi=ai1bi2bi1.b_i=a_{i-1}-b_{i-2}-b_{i-1}.

若某次推出的 bib_i 不在 {0,1}\{0,1\} 中,则该枚举无效。最后还需要检查右端边界:

an=bn1+bn.a_n=b_{n-1}+b_n.

两个初始状态至多产生两个候选方案,统计其中合法的方案数即可。

【边界检查】

固定 b0=0b_0=0 已经处理了左端边界;枚举 b1b_1 后,递推时每个 bib_i 都被唯一确定,若不在 {0,1}\{0,1\} 中立刻舍弃。最后检查 an=bn1+bna_n=b_{n-1}+b_n,等价于验证右端边界 bn+1=0b_{n+1}=0

【复杂度】

只有两种初值。每次枚举线性递推,时间复杂度为 O(n)\mathcal{O}(n),空间复杂度为 O(n)\mathcal{O}(n)

【参考代码】

/*Author:EhundateghDate:2026/7/23Name:skycall.cppYou steal,I kill.*/#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 200010using namespace std;int Line[MAXN],n,Ans=0;int Dp[MAXN];bool Tag=1;void Solve(){    Ans=0;Tag=1;    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&Line[i]);    }    Dp[0]=0;Dp[1]=0;    for(int i=2;i<=n;i++){        if(Line[i-1]-Dp[i-1]-Dp[i-2]>1||Line[i-1]-Dp[i-1]-Dp[i-2]<0) {Tag=0; break;}        else if(Line[i-1]-Dp[i-1]-Dp[i-2]==1){            Dp[i]=1;        }        else if(Line[i-1]-Dp[i-1]-Dp[i-2]==0){            Dp[i]=0;        }    }    if(Line[n]!=Dp[n-1]+Dp[n]) Tag=0;    if(Tag) Ans++;    Dp[0]=0;Dp[1]=1; Tag=1;    for(int i=2;i<=n;i++){        if(Line[i-1]-Dp[i-1]-Dp[i-2]>1||Line[i-1]-Dp[i-1]-Dp[i-2]<0) {Tag=0; break;}        else if(Line[i-1]-Dp[i-1]-Dp[i-2]==1){            Dp[i]=1;        }        else if(Line[i-1]-Dp[i-1]-Dp[i-2]==0){            Dp[i]=0;        }    }    if(Line[n]!=Dp[n-1]+Dp[n]) Tag=0;    if(Tag) Ans++;    printf("%d\n",Ans);    return;}int main(){    int c,T;    freopen("skycall.in","r",stdin);    freopen("skycall.out","w",stdout);    scanf("%d%d",&c,&T);    while(T-->0) Solve();    return 0;}