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

本文探讨了一道关于大进制数周期性的编程题,通过数论优化、矩阵快速幂到递推策略的转变,解决了在大范围数据下高效计算满足特定模条件的数的个数问题。作者详细介绍了从矩阵优化的困境,到利用子集容斥和多步递推的精妙优化过程。
摘要由CSDN通过智能技术生成

题目链接

题意

对于所有的 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';
	}
}

总结

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值