bzoj2032 [国家集训队]密码系统

题目链接

题意

对于所有的 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 B3 M ⩽ 120 M\leqslant 120 M120
对于另外 40 % 40\% 40% 的数据, B ⩽ 10 B\leqslant 10 B10 M ⩽ 40 M\leqslant 40 M40
对于 100 % 100\% 100% 的数据, N ⩽ 1 0 9 N\leqslant 10^9 N109 V ⩽ M V\leqslant M VM

解析

看见 N ⩽ 1 0 9 N\leqslant 10^9 N109,果断考虑数论或者矩阵快速幂优化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(0d<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+dj(modM),S{j}=Sfi1,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+dj(modM)fi1,k
目标状态为 f n − 1 , V f_{n-1,V} fn1,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+j2j(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} fn1,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';
	}
}

总结

一个很神奇的题,从容斥的应用到矩阵的优化成递推都是非常精妙的优化,难得一见的好题。

posted @ 2022-08-09 20:18  Velix  阅读(49)  评论(0)    收藏  举报