Semi-palindromic
Description
对于一个不含前导
0
0
的十进制非负整数,如果它是的倍数,且出现次数为奇数的某个数字最多只有一个(前导
0
0
不算),那么称它是牛逼数。
给定 ,求小于
10n
10
n
的牛逼数有多少个,输出答案模
109+7
10
9
+
7
。
Data Constraints
1≤m≤16 1 ≤ m ≤ 16 1≤n≤1018 1 ≤ n ≤ 10 18
Solution
这是我打过的为数不多的
FWT
F
W
T
题了。
先考虑一个
O(210nm)
O
(
2
10
n
m
)
的做法,设
fi,j,S
f
i
,
j
,
S
表示构造至i位,前
i
i
位模为
j
j
,每个数字出现次数的奇偶性的二进制状态为的方案数,转移很简单,统计答案也很简单。
但这样做显然会超时。
我们再设一个
fl,r,j,S
f
l
,
r
,
j
,
S
表示构造完第
l
l
~位,第
l
l
~位模
m
m
为,每个数字出现次数的奇偶性的二进制状态为
S
S
的方案数,假设我们已经知道了,那我们是可以快速求出
f1+i,2i,j,S
f
1
+
i
,
2
i
,
j
,
S
,具体转移为
f1,i,j,S
f
1
,
i
,
j
,
S
可转移到
fi+1,2i, j∗10i % m,S
f
i
+
1
,
2
i
,
j
∗
10
i
%
m
,
S
,然后
f1,i
f
1
,
i
和
fi+1,2i
f
i
+
1
,
2
i
进行分层异或卷积,就可以得到
f1,2i
f
1
,
2
i
,利用这个思想就可以倍增的求了,异或卷积的实现用
FWT
F
W
T
。
但要注意到一个地方,两个
f
f
卷积的时候,前导将有可能改变
0
0
的个数的奇偶性,因此直接这么做是行不通的。
对此,我们可以设和
g
g
分别表示有奇数/偶数个前导时的
dp
d
p
数组,这样就可以了。
时间复杂度 O(10m 210log n) O ( 10 m 2 10 l o g n )
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)
using namespace std;
typedef long long ll;
const ll N=1025,M=25,mo=1e9+7,OP=(mo+1)>>1,mm=1023;
ll f[M][N],g[M][N],k1[M][N],k2[M][N],f1[M][N],g1[M][N];
ll v1[M][N],v2[M][N];
int ok[N];
ll w[N];
ll n;
int m,T;
inline ll ksm(ll o,ll t,ll mmm)
{
ll y=1;
for(;t;t>>=1,o=o*o%mmm)
if(t&1)y=y*o%mmm;
return y;
}
inline ll mo1(ll p)
{return p<mo?p:(p-mo);}
void dft(int a)
{
for(int m=2;m<=mm+1;m<<=1){
ll keep;
register int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=f[a][i+half+j];
f[a][i+half+j]=mo1(f[a][j+i]-keep+mo);
f[a][i+j]=mo1(keep+f[a][i+j]);
}
}
for(int m=2;m<=mm+1;m<<=1){
ll keep;
int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=g[a][i+half+j];
g[a][i+half+j]=mo1(g[a][j+i]-keep+mo);
g[a][i+j]=mo1(keep+g[a][i+j]);
}
}
for(int m=2;m<=mm+1;m<<=1){
ll keep;
int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=f1[a][i+half+j];
f1[a][i+half+j]=mo1(f1[a][j+i]-keep+mo);
f1[a][i+j]=mo1(keep+f1[a][i+j]);
}
}
for(int m=2;m<=mm+1;m<<=1){
ll keep;
int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=g1[a][i+half+j];
g1[a][i+half+j]=mo1(g1[a][j+i]-keep+mo);
g1[a][i+j]=mo1(keep+g1[a][i+j]);
}
}
}
void idft(int a)
{
for(int m=2;m<=mm+1;m<<=1){
ll keep;
int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=v1[a][i+half+j];
v1[a][i+half+j]=(v1[a][i+j]-keep+mo)*OP%mo;
v1[a][i+j]=(keep+v1[a][i+j])*OP%mo;
}
}
for(int m=2;m<=mm+1;m<<=1){
ll keep;
int half=m>>1;
for(int i=0;i<=mm;i+=m)
fo(j,0,half-1){
keep=v2[a][i+half+j];
v2[a][i+half+j]=(v2[a][i+j]-keep+mo)*OP%mo;
v2[a][i+j]=(keep+v2[a][i+j])*OP%mo;
}
}
}
void solve(ll o)
{
w[0]=0;
for(;o;o>>=1)w[++w[0]]=o;
fo(i,0,m-1)fo(l,0,mm)f[i][l]=g[i][l]=0;
fo(i,1,9)f[i%m][ok[i]]=1;
fd(ko,w[0]-1,1){
fo(i,0,m-1)fo(l,0,mm)k1[i][l]=f[i][l],f1[i][l]=0;
fo(i,0,m-1)fo(l,0,mm)k2[i][l]=g[i][l],g1[i][l]=0;
if(w[ko+1]&1)
fo(i,0,m-1)fo(l,0,mm)swap(k1[i][l],k2[i][l]);
int yu=ksm(10,w[ko+1],m);
fo(i,0,m-1)
{
int e=i*yu%m; int u=w[ko+1]&1;
fo(l,0,mm)f1[e][l]=mo1(f1[e][l]+f[i][l]),k1[e][l^u]=mo1(k1[e][l^u]+f[i][l]);
fo(l,0,mm)g1[e][l]=mo1(g1[e][l]+g[i][l]),k2[e][l^u]=mo1(k2[e][l^u]+g[i][l]);
}
fo(i,0,m-1)fo(l,0,mm)v1[i][l]=v2[i][l]=0;
fo(i,0,m-1)
fo(l,0,mm)if(l&1)swap(g[i][l^1],g[i][l]);
fo(i,0,m-1)dft(i);
fo(i,0,m-1)fo(j,0,m-1){
int k=(i+j)%m;
fo(l,0,mm)v1[k][l]=(v1[k][l]+f1[i][l]*(f[j][l]+g[j][l]))%mo;
fo(l,0,mm)v2[k][l]=(v2[k][l]+g1[i][l]*(f[j][l]+g[j][l]))%mo;
}
fo(i,0,m-1)idft(i);
fo(i,0,m-1)fo(l,0,mm)f[i][l]=mo1(v1[i][l]+k1[i][l]),g[i][l]=mo1(v2[i][l]+k2[i][l]);
if(w[ko]&1)
{
int e=yu*yu%m;
fo(i,0,m-1)fo(l,0,mm)v1[i][l]=g[i][l],v2[i][l]=f[i][l];
fo(j,1,9)
fo(i,0,m-1){
int k=(i+j*e)%m;
fo(l,0,mm)v1[k][l^ok[j]]=(v1[k][l^ok[j]]+f[i][l])%mo;
fo(l,0,mm)v1[k][l^ok[j]^1]=(v1[k][l^ok[j]^1]+g[i][l])%mo;
}
fo(j,1,9)v1[j*e%m][ok[j]^(w[ko]&1)^1]++;
fo(i,0,m-1)fo(l,0,mm)f[i][l]=v1[i][l],g[i][l]=v2[i][l];
}
}
}
int main()
{
cin>>T;
ok[0]=1;
fo(i,1,9)ok[i]=ok[i-1]<<1;
fo(tt,1,T){
cin>>m>>n;
solve(n);
ll ans=f[0][0]+f[0][1]+1;
fo(i,1,9)ans=(ans+f[0][ok[i]])%mo;
ans=ans+g[0][0]+g[0][1];
fo(i,1,9)ans=(ans+g[0][ok[i]])%mo;
ans=ans%mo;
cout<<ans<<endl;
}
}