JZOJ1898. 【2010集训队出题】密码系统

Description

  • Lambda受任于某情报站,他的工作是获取敌人情报。一次他在破解密码系统时,得到了一个N位B进制数φ,满足φ≡V (mod M)。他发现组成φ的数字很奇特。为了验证φ的特殊性,他将所有模M为V的N位B进制数,按照各数位构成的集合分类,并想知道每一类数各有多少个。
    在这里插入图片描述

Solution

  • 不难想到矩乘,枚举所选集合再进行矩乘,f[i]表示余数为i的方案数,但是这样并不能保证集合中的都被选了,所以实际算出来需要容斥一下。
  • 再考虑矩乘的复杂度,如果直接M3是会爆的。但是我们发现将i和j合并起来时转移到的状态是一定的并且可以算出来,所以M2转移就好了。
  • 为什么这题这么特殊呢?
  • 我们一般的矩乘要枚举起点终点,而这题却枚举起点和终点间的转移,并且显然转移比那个枚举地更少更好。所以说这种情况仅当转移状态十分好枚举地时候才适用。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define maxm 125
#define mo 10007
using namespace std;

int n,B,M,V,S,T,i,j,k,p;
int f[maxm],ff[maxm],g[maxm],gg[maxm];
int F[1<<11];

int main(){
	scanf("%d%d%d%d",&n,&B,&M,&V);
	for(S=1;S<1<<B;S++){
		memset(f,0,sizeof(f));
		memset(g,0,sizeof(g));
		for(i=0;i<B;i++) if ((S>>i)&1) g[i%M]++;
		for(i=1;i<B;i++) if ((S>>i)&1) f[i%M]++;
		p=B%M;
		for(k=n-1;k;k/=2){
			if (k&1){
				memset(ff,0,sizeof(ff));
				for(i=0;i<M;i++) if (f[i]) for(j=0;j<M;j++) if (g[j])
					(ff[(i*p+j)%M]+=f[i]*g[j])%=mo;
				memcpy(f,ff,sizeof(ff));
			}
			memset(gg,0,sizeof(gg));
			for(i=0;i<M;i++) if (g[i]) for(j=0;j<M;j++) if (g[j])
				(gg[(i*p+j)%M]+=g[i]*g[j]%mo)%=mo;
			memcpy(g,gg,sizeof(gg));
			p=p*p%M;
		}
		F[S]=f[V];
		for(T=0;T<S;T++) if ((T&S)==T) 
			F[S]=(F[S]-F[T]+mo)%mo;
		for(i=B-1;i>=0;i--) if ((S>>i)&1) printf("%d",i);
		printf(" %d
",F[S]);
	}
}
原文地址:https://www.cnblogs.com/DeepThinking/p/11700909.html