题意
对于所有的 N N N 位 B B B 进制数 φ φ φ,按照各数位构成的集合分类,求每一类各有多少个数满足 φ ≡ V ( m o d M ) φ≡V (\!\!\!\!\mod M) φ≡V(modM),答案对 1000 1000 1000。
数据范围:
对于 60 % 60\% 60% 的数据, B ⩽ 3 B\leqslant 3 B⩽3, M ⩽ 120 M\leqslant 120 M⩽120;
对于另外 40 % 40\% 40% 的数据, B ⩽ 10 B\leqslant 10 B⩽10, M ⩽ 40 M\leqslant 40 M⩽40;
对于 100 % 100\% 100% 的数据, N ⩽ 1 0 9 N\leqslant 10^9 N⩽109, V ⩽ M V\leqslant M V⩽M。
解析
看见
N
⩽
1
0
9
N\leqslant 10^9
N⩽109,果断考虑数论或者矩阵快速幂优化DP,考虑到每个数位直接联系不大,应该为矩阵快速幂。
设
f
i
,
j
,
S
f_{i,j,S}
fi,j,S 表示递推到第
i
i
i 位,余数为
j
j
j,使用的数位状态为
S
S
S 时的方案数,如
f
2
,
1
,
(
101
)
2
f_{2,1,(101)_2}
f2,1,(101)2 表示递推到了第
2
2
2 位,余数为
1
1
1,使用了
0
0
0 和
2
2
2 两个数字的方案数。
设下一位填入的数为
d
(
0
⩽
d
<
b
)
d(0\leqslant d<b)
d(0⩽d<b),可得:
f
i
,
j
,
S
=
∑
b
k
+
d
≡
j
(
m
o
d
M
)
,
S
′
∪
{
j
}
=
S
f
i
−
1
,
k
,
S
′
f_{i,j,S}=\sum\limits_{bk+d≡j (\!\!\!\!\mod M),S'\cup \{j\}=S} f_{i-1,k,S'}
fi,j,S=bk+d≡j(modM),S′∪{j}=S∑fi−1,k,S′
对
i
i
i 进行优化,建立一个大小为
M
2
B
M2^B
M2B 的矩阵加速递推,时间复杂度为
O
(
8
B
M
3
l
o
g
N
)
O(8^BM^3logN)
O(8BM3logN),无法通过。
时间复杂度的瓶颈在于过于巨大的矩阵大小,可以看到这个
2
B
2^B
2B 过于恶臭,考虑优化它。
对于一个状态
S
S
S,可以发现它要求每个数位至少存在一次,所以占据了巨大的空间。
如果改为求出一个状态
S
S
S 的所有子集,那么方程中的
S
′
S'
S′ 就会等于
S
S
S,我们就可以省下这一大部分的矩阵大小。
具体的,从小到大枚举
S
S
S,设
f
i
,
j
f_{i,j}
fi,j 表示递推到
i
i
i 为,余数为
j
j
j 时的方案数。可得:
f
i
,
j
=
∑
b
k
+
d
≡
j
(
m
o
d
M
)
f
i
−
1
,
k
f_{i,j}=\sum\limits_{bk+d≡j (\!\!\!\!\mod M)} f_{i-1,k}
fi,j=bk+d≡j(modM)∑fi−1,k
目标状态为
f
n
−
1
,
V
f_{n-1,V}
fn−1,V,最后不可以填
0
0
0,需特殊考虑。
对于每个
S
S
S 具体的状态,直接枚举子集容斥掉即可求出。
时间复杂度为
O
(
2
B
M
3
l
o
g
N
+
3
B
)
O(2^BM^3logN+3^B)
O(2BM3logN+3B),还是不太行。
既然矩阵行不通,那就不使用矩阵,用另外的方式来进行递推。
观察之前的转移,每次一点一点的转移非常的缓慢,能不能多走几步呢?
可以。依然沿用先前
f
i
,
j
f_{i,j}
fi,j 的定义,可得递推:
f
i
,
j
=
∑
j
1
b
i
2
+
j
2
≡
j
(
m
o
d
M
)
f
i
1
,
j
1
f
i
2
,
j
2
f_{i,j}=\sum\limits_{j_1b^{i_2}+j_2≡j (\!\!\!\!\mod M)}f_{i_1,j_1}f_{i_2,j_2}
fi,j=j1bi2+j2≡j(modM)∑fi1,j1fi2,j2
其中
i
1
+
i
2
=
i
i_1+i_2=i
i1+i2=i,不做硬性要求。
那么可以沿用快速幂的思想,用多个
f
2
i
,
x
f_{2^i,x}
f2i,x 来拼凑出
f
n
−
1
,
V
f_{n-1,V}
fn−1,V。
那么一次合并的时间复杂度为
O
(
M
2
)
O(M^2)
O(M2),总时间复杂度即为
O
(
2
B
M
2
l
o
g
N
+
3
B
)
O(2^BM^2logN+3^B)
O(2BM2logN+3B),可以通过。
Code
#include<bits/stdc++.h>
#define mod 10007
using namespace std;
int n,b,m,V;
int dp[130],t[130],_dp[130],_ans[1024],ans[1024];
int main()
{
cin>>n>>b>>m>>V;
n--;
for(int i=1;i<(1<<b);i++)
{
memset(t,0,sizeof t);
memset(dp,0,sizeof dp);
for(int j=0;j<b;j++)
if(i&(1<<j))
{
t[j%m]++;
if(j!=0)dp[j%m]++;
}
for(int j=n,x=b%m;j;j>>=1,x=(x*x)%m)
{
if(j&1)
{
memset(_dp,0,sizeof _dp);
for(int u=0;u<m;u++)
for(int v=0;v<m;v++)
_dp[(u*x+v)%m]=(_dp[(u*x+v)%m]+dp[u]*t[v])%mod;
memcpy(dp,_dp,sizeof _dp);
}
memset(_dp,0,sizeof _dp);
for(int u=0;u<m;u++)
for(int v=0;v<m;v++)
_dp[(u*x+v)%m]=(_dp[(u*x+v)%m]+t[u]*t[v])%mod;
memcpy(t,_dp,sizeof _dp);
}
ans[i]=dp[V];
for(int j=i&(i-1);j;j=(j-1)&i)
ans[i]=(ans[i]-ans[j]+mod)%mod;
for(int j=b-1;j>=0;j--)
if(i&(1<<j))cout<<j;
cout<<' '<<ans[i]<<'\n';
}
}
总结
一个很神奇的题,从容斥的应用到矩阵的优化成递推都是非常精妙的优化,难得一见的好题。