[atAGC045C]Range Set

首先我们可以把所有位置都变为1,因此不妨假设$ale b$

一个字符串$s$合法当且仅当:将其中每一段长度不小于$a$的0变成1后,存在一段1的长度都不小于$b$

证明:我们称$S_{a,b}$为通过$(a,b)$能产生的字符串所构成的集合,则有$S_{a,b}=S_{b,a}$

称原来的字符串为$s$,将每一段长度不小于$a$的0变成1后称为$s'$,则若$s'in S_{a,b}$则可以得到$sin S_{a,b}$,同时若有$sin S_{b,a}$又可以得到$s'in S_{b,a}$,即可得$sin S_{a,b}$等价于$s'in S_{a,b}$

接下来,就是证明$s'in S_{a,b}$当且仅当存在一段1的长度都不小于$b$

必要性:考虑$s'$中不存在一段0的长度不小于$a$,又不存在一段1的长度不小于$b$,对最后一次操作分类讨论即可(初始$nge a,b$,也满足条件)

充分性:将长度不小于$b$的一段1变为0,则若可以得到这个串则一定可以得到$s'$,然后由于$bge a$,再把这段0和左右两边原来的0合并起来,长度不小于$a$,根据上面的结论,可以将这一段变为1

重复此操作,每一次操作必然减少一段0,因此最终即所有位置都为1,显然可以做到

统计不合法的字符串数量,假设已经确定$s'$,去统计对应为$s'$的$s$数量,考虑dp

预处理出用$g_{l}$表示将这段1中若干个位置改为0,且每一段0的长度都不小于$a$的方案数,再用用$f_{i,0/1}$表示前$i$个数,第$i$个数为0或1,简单转移即可

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 5005
 4 #define mod 1000000007
 5 int n,a,b,ans,g[N],f[N][N];
 6 int main(){
 7     scanf("%d%d%d",&n,&a,&b);
 8     if (a>b)swap(a,b);
 9     for(int i=0;i<a;i++)g[i]=1;
10     for(int i=a;i<=n;i++){
11         g[i]=(g[i-1]+1)%mod;
12         for(int j=a;j<i;j++)g[i]=(g[i]+g[i-j-1])%mod;
13     }
14     f[0][0]=f[0][1]=1;
15     for(int i=1;i<=n;i++)
16         for(int j=0;j<i;j++)
17             if (i-j<b){
18                 int l=i-j-2;
19                 if (!j)l++;
20                 if (i==n)l++;
21                 f[i][1]=(f[i][1]+1LL*f[j][0]*g[max(l,0)])%mod;
22                 if (i-j<a)f[i][0]=(f[i][0]+f[j][1])%mod;
23             }
24     ans=1;
25     for(int i=0;i<n;i++)ans=ans*2%mod;
26     ans=(ans+mod-(f[n][0]+f[n][1])%mod)%mod;
27     printf("%d",ans);
28 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/14172855.html