Description
用N个不同的字符(编号1 - N),组成一个字符串,有如下要求:
(1) 对于编号为i的字符,如果2 * i > n,则该字符可以作为结尾字符。如果不作为结尾字符而是中间的字符,则该字符后面可以接任意字符。
(2) 对于编号为i的字符,如果2 * i <= n,则该字符不可以作为结尾字符。作为中间字符,那么后面接的字符编号一定要 >= 2 * i。
问有多少长度为M且符合条件的字符串,由于数据很大,只需要输出该数Mod 10^9 + 7的结果。
例如:N = 2,M = 3。则abb, bab, bbb是符合条件的字符串,剩下的均为不符合条件的字符串。
n
≤
1
0
6
,
  
m
≤
1
0
18
n\le 10^6,\; m\le 10^{18}
n≤106,m≤1018
Solution
考虑这个问题的弱化版本,
m
≤
1
0
6
m\le 10^6
m≤106的时候要咋做
容易发现2条件只会有log(m)次,并且相邻两次间隔不超过log(m)个,我们可以用这个性质来分段dp
设g[i,j]表示长度恰好为i,最后一位不大于j的一段的方案数。这里一段的定义是只有最后一个*2>n
设s[i]表示长度为i的段数有多少种,f[i]表示长度为i的合法串有多少种,那么有
f
[
i
]
=
∑
j
=
1
i
f
[
i
−
j
]
⋅
s
[
j
]
f[i]=\sum_{j=1}^i{f[i-j]\cdot s[j]}
f[i]=∑j=1if[i−j]⋅s[j]
这样做就是
O
(
n
(
log
(
m
)
+
m
)
)
O(n(\log(m)+m))
O(n(log(m)+m))的
然后可以发现求f的时候只有后60位有用,那么就是抠出来然后矩阵快速幂了
当然你也可以发现这个还可以多项式生成函数来搞,这里就不写了
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
typedef long long LL;
const int MOD=1000000007;
const int N=1000005;
int f[N],g[2][N],s[N];
struct Mat {
int r[61][61],n,m;
Mat() {fill(r,0);}
int* operator [](int x) {
return r[x];
}
Mat operator *(Mat B) {
Mat A=*this,C;
rep(i,1,A.n) rep(j,1,A.m) {
C[i][j]=0;
rep(k,1,B.m) C[i][j]=(C[i][j]+1LL*A[i][k]*B[k][j]%MOD)%MOD;
}
C.n=A.n,C.m=B.m; return C;
}
Mat operator ^(LL dep) {
Mat X=*this,C=X; dep--;
for (;dep;dep>>=1) {
if (dep&1) C=C*X;
X=X*X;
}
return C;
}
} ;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void solve(int n,LL m) {
f[0]=1;
rep(i,1,std:: min(m,60LL)) rep(j,1,std:: min(i,60)) {
f[i]=(f[i]+1LL*f[i-j]*s[j]%MOD)%MOD;
}
}
Mat A,B;
int main(void) {
int n; LL m; scanf("%d%lld",&n,&m);
LL wjp=std:: min(m,60LL);
rep(i,0,n) g[0][i]=1;
rep(i,1,wjp) {
fill(g[i&1],0);
rep(j,1,n) g[i&1][j]=(g[i&1][j-1]+g[(i-1)&1][j/2])%MOD;
s[i]=(g[i&1][n]-g[i&1][n/2]+MOD)%MOD;
}
solve(n,m);
if (m<=60) return 0&printf("%d\n", f[m]);
A.n=A.m=60; B.n=1,B.m=60;
rep(i,1,60) B[1][i]=f[i];
rep(i,1,59) A[i+1][i]=1;
rep(i,1,60) A[i][60]=s[61-i];
A=A^(m-1); B=B*A;
printf("%d\n", B[1][1]);
return 0;
}