CCPC Strings
题意:一个字符串中只包含
C
,
P
C,P
C,P 两个字符,定义
C
C
P
C
n
e
s
s
CCPCness
CCPCness 是一个字符串中不重叠的
“
C
C
P
C
”
“CCPC”
“CCPC” 字符子串的最大数量, 给定一个
n
n
n,求长度为
n
n
n 的
2
n
2^n
2n 个所有字符串的
C
C
P
C
n
e
s
s
CCPCness
CCPCness 之和。
题解:
计算贡献+容斥+推公式
bm线性递推
(了解了一下bm算法,实在太牛了
我先谈谈我的思路:首先我考虑计算一个
C
C
P
C
CCPC
CCPC 的贡献(不考虑重叠多计算的部分),长度为
n
n
n 的字符串有
n
−
3
n-3
n−3 个位置可以放置
C
C
P
C
CCPC
CCPC,然后剩余的位置随便放,就可以得到公式
(
n
−
3
)
×
2
n
−
4
(n-3)\times2^{n-4}
(n−3)×2n−4。
然后考虑去重,我们可以发现
n
=
7
n=7
n=7 时,
C
C
P
C
C
P
C
CCPCCPC
CCPCCPC 这个字符串多计算了一个贡献,然后容斥去重,减掉
C
C
P
C
C
P
C
CCPCCPC
CCPCCPC 所有的贡献,然后加上
C
C
P
C
C
P
C
C
P
C
CCPCCPCCPC
CCPCCPCCPC 所有的贡献
.
.
.
...
...
n
<
4
n<4
n<4,输出
0
0
0
n
>
=
4
n>=4
n>=4
a
n
=
∑
i
=
1
⌊
n
3
⌋
(
−
1
)
i
−
1
(
n
−
3
i
)
2
n
−
3
i
−
1
a_n=\sum_{i=1}^{\lfloor\frac{n}{3}\rfloor}(-1)^{i-1}(n-3i)2^{n-3i-1}
an=i=1∑⌊3n⌋(−1)i−1(n−3i)2n−3i−1
令
n
=
n
−
3
n=n-3
n=n−3,
n
>
=
7
n>=7
n>=7
a
n
−
3
=
∑
i
=
1
⌊
n
−
3
3
⌋
(
−
1
)
i
−
1
(
n
−
3
−
3
i
)
2
n
−
3
−
3
i
−
1
=
∑
i
=
1
⌊
n
3
⌋
−
1
(
−
1
)
i
−
1
(
n
−
3
(
i
+
1
)
)
2
n
−
3
(
i
+
1
)
−
1
=
∑
i
=
2
⌊
n
3
⌋
(
−
1
)
i
(
n
−
3
i
)
2
n
−
3
i
−
1
a_{n-3}=\sum_{i=1}^{\lfloor{\frac{n-3}{3}}\rfloor}(-1)^{i-1}(n-3-3i)2^{n-3-3i-1} =\sum_{i=1}^{{\lfloor{\frac{n}{3}}\rfloor}-1}(-1)^{i-1}(n-3(i+1))2^{n-3(i+1)-1} =\sum_{i=2}^{{\lfloor{\frac{n}{3}}\rfloor}}(-1)^{i}(n-3i)2^{n-3i-1}
an−3=i=1∑⌊3n−3⌋(−1)i−1(n−3−3i)2n−3−3i−1=i=1∑⌊3n⌋−1(−1)i−1(n−3(i+1))2n−3(i+1)−1=i=2∑⌊3n⌋(−1)i(n−3i)2n−3i−1
相加右边只剩下第一项,然后就得到数列的递推公式了
a
n
+
a
n
−
3
=
(
n
−
3
)
2
n
−
4
(
n
>
=
4
)
a_n+a_{n-3}=(n-3)2^{n-4}(n>=4)
an+an−3=(n−3)2n−4(n>=4)
然后根据递推式求数列通项,具体求法鸽了,看大佬博客吧
也可以直接分析得到递推式:传送门
得到递推式我们就可以用矩阵快速幂解决:传送门
但是矩阵快速幂会TLE,题解里的出题人吐槽
菜鸡的我并没有看懂咋优化
(看了上边博主更新后的代码我懂这个优化了,就是把系数矩阵的次幂预处理出来,矩阵快速幂的时候直接用就好了
然后
因为我们可以
f
o
r
for
for 循环容斥求
n
n
n 比较小的情况
所以直接莽BM算法求线性递推公式(太好用了
code:
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define SZ(x) ((int)(x).size())
typedef long long ll;
typedef vector<ll> VI;
const ll mod=1000000007;
ll n;
namespace linear_seq
{
const int N=10010;
ll res[N],base[N],_c[N],_md[N];
vector<int> Md;
ll powmod(ll a,ll b) {
ll res=1;a%=mod; for(;b;b>>=1){
if(b&1)res=res*a%mod;a=a*a%mod;
}return res;
}
ll mul(ll *a,ll *b,int k)
{
rep(i,0,k+k) _c[i]=0;
rep(i,0,k) if (a[i]) rep(j,0,k) _c[i+j]=(_c[i+j]+a[i]*b[j])%mod;
for (int i=k+k-1;i>=k;i--) if (_c[i])
rep(j,0,SZ(Md)) _c[i-k+Md[j]]=(_c[i-k+Md[j]]-_c[i]*_md[Md[j]])%mod;
rep(i,0,k) a[i]=_c[i];
}
ll solve(ll n,VI a,VI b)
{
ll ans=0,pnt=0;
int k=SZ(a);
assert(SZ(a)==SZ(b));
rep(i,0,k) _md[k-1-i]=-a[i];_md[k]=1;
Md.clear();
rep(i,0,k) if (_md[i]!=0) Md.push_back(i);
rep(i,0,k) res[i]=base[i]=0;
res[0]=1;
while ((1ll<<pnt)<=n) pnt++;
for (int p=pnt;p>=0;p--)
{
mul(res,res,k);
if ((n>>p)&1)
{
for (int i=k-1;i>=0;i--) res[i+1]=res[i];res[0]=0;
rep(j,0,SZ(Md)) res[Md[j]]=(res[Md[j]]-res[k]*_md[Md[j]])%mod;
}
}
rep(i,0,k) ans=(ans+res[i]*b[i])%mod;
if (ans<0) ans+=mod;
return ans;
}
VI BM(VI s)
{
VI C(1,1),B(1,1);
int L=0,m=1,b=1;
rep(n,0,SZ(s))
{
ll d=0;
rep(i,0,L+1) d=(d+(ll)C[i]*s[n-i])%mod;
if (d==0) ++m;
else if (2*L<=n)
{
VI T=C;
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
L=n+1-L; B=T; b=d; m=1;
}
else
{
ll c=mod-d*powmod(b,mod-2)%mod;
while (SZ(C)<SZ(B)+m) C.pb(0);
rep(i,0,SZ(B)) C[i+m]=(C[i+m]+c*B[i])%mod;
++m;
}
}
return C;
}
ll gao(VI a,ll n)
{
VI c=BM(a);
c.erase(c.begin());
rep(i,0,SZ(c)) c[i]=(mod-c[i])%mod;
return solve(n,c,VI(a.begin(),a.begin()+SZ(c)));
}
};
ll q_pow(ll a, ll n){
ll ans=1;while(n){
if(n & 1) ans=(ans*a)%mod;a=(a*a)%mod;n>>=1;
}return ans;
}
int main() {
vector<ll>v;
for(int k=1;k<=20;k++){
ll ans = 0;
n = k;
if(n < 4) ans = 0;
else
{
ll tt = 0;
if(n >= 7)
{
for(int i = 7; i <= n; i += 3)// 容斥
{
if(((i - 4) / 3) & 1)// +
tt = ((n - i + 1) * q_pow(2, n - i) % mod + tt) % mod;
else
tt = (tt + mod - ((n - i + 1) * q_pow(2, n - i))) % mod;
}
}
ans = ((n-3)*q_pow(2,n-4)%mod-tt+mod)%mod;// 因为减完可能是负的,记得加mod再取模
}
//cout << ans << endl;
v.push_back(ans);
}
int t;
scanf("%d",&t);
while (t--) {
scanf("%lld",&n);
printf("%lld\n",linear_seq::gao(v,n-1));
}
}
更一下矩阵快速幂的解法
上边博主的构造思路:其实就是想
(
n
−
3
)
∗
2
n
−
4
(n-3)*2^{n-4}
(n−3)∗2n−4 怎么变到
(
n
−
2
)
∗
2
n
−
3
(n-2)*2^{n-3}
(n−2)∗2n−3,
(
n
−
3
)
∗
2
n
−
4
(n-3)*2^{n-4}
(n−3)∗2n−4 可以先乘个
2
2
2 可以变成
(
n
−
3
)
∗
2
n
−
3
(n-3)*2^{n-3}
(n−3)∗2n−3 。然后要在
a
a
a 矩阵后加一个元素
2
n
−
3
2^{n-3}
2n−3 来辅助最后的变换。
递推式
f
n
+
f
n
−
3
=
(
n
−
3
)
∗
2
n
−
4
f_n+f_{n-3}=(n-3)*2^{n-4}
fn+fn−3=(n−3)∗2n−4
引用一下博主的图片
然后就得到了系数矩阵
A
A
A
最后引用一下大佬的代码
#include<bits/stdc++.h>
#define ll long long
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int mod = 1e9+7;
const int N = 5;
struct Matrix{
ll s[N+1][N+1];
Matrix(){
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
s[i][j]=0;
}
};
Matrix mul(Matrix a,Matrix b){
Matrix res;
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
for(int k=1;k<=N;k++){
res.s[i][j]+=(a.s[i][k]*b.s[k][j]+mod)%mod;
res.s[i][j]%=mod;
}
}
}
return res;
}
Matrix C[30];
Matrix fastpow(ll b){
Matrix res;
for(int i=1;i<=N;i++) res.s[i][i]=1;
int cnt=0;
while(b){
if(b&1){
res=mul(res,C[cnt]);
}
b>>=1;
cnt++;
}
return res;
}
int main(){
Matrix A,f;
A.s[3][1]=-1;
A.s[1][2]=A.s[2][3]=A.s[4][1]=A.s[5][4]=1;
A.s[4][4]=A.s[5][5]=2;
f.s[1][1]=12;
f.s[1][2]=4;
f.s[1][3]=1;
f.s[1][4]=32;
f.s[1][5]=16;
//预处理A^2,A^4,A^8,用C矩阵存起来
C[0]=A;
for(int i=1;i<=30;i++){
C[i]=mul(C[i-1],C[i-1]);
}
int t;
cin>>t;
ll n;
while(t--){
cin>>n;
if(n<4) cout<<0<<endl;
else if(n==4) cout<<1<<endl;
else if(n==5) cout<<4<<endl;
else{
Matrix B, ans;
B = fastpow(n-6);
ans = mul(f, B);
cout<<ans.s[1][1]<<endl;
}
}
return 0;
}