[SCOI2007]排列--状态压缩DP+余数的性质

时光总嘲笑我的痴心妄想 提交于 2019-12-06 20:15:06

Luogu 4163

在这里插入图片描述

题目分析:

  • 这个题要求的排列是不能重复的,所有我们先假定ss的每位数不相等,最后统计答案数去掉重复的就OKOK

  • 考虑枚举排列的过程:
    有一个s:abcs串:abc,我们已经排列到了bajba,此时的余数为j
    当我们把cc加进来时,变成了bacbac,相当于就是ba10+cba*10+c
    而余数又会怎么变化呢?
    j(j10+c)j变成了(j*10+c)%dd

  • 我们定义f[i][j]iisf[i][j]表示状态为i(i的每一位表示s串中的数字选或不选)时j余数为j的方案数

  • 边界f[i][j]=0,f[0][0]=1f[i][j]=0,f[0][0]=1

  • 状态转移:
    f[iUx][(j10+a[x])f[iUx][(j*10+a[x])%d]+=f[i][j]d]+=f[i][j]

Code:

#include <bits/stdc++.h>
using namespace std;
#define maxd 1010
#define maxn 20
#define max_size 1100

int a[maxn],T,d,n,f[max_size][maxd],cnt[maxn];
char s[maxn];

inline int read_() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x*f;
}

inline void clean_() {
	memset(f,0,sizeof(f));
	memset(cnt,0,sizeof(cnt));
}

inline void DP_() {
	f[0][0]=1;
	for(int i=0;i<(1<<n);++i) {
		for(int j=0;j<d;++j) {
			for(int k=1;k<=n;++k) {
				if( ! ( i &  (1<<(k-1)) ) ) {
					f[i|(1<<(k-1))][(j*10+a[k])%d]+=f[i][j];
				}
			}
		}
	}
	int ans=f[(1<<n)-1][0];
	for(int i=0;i<=9;++i) {
		for(int j=2;j<=cnt[i];++j) {
			ans/=j;
		}
	}
	printf("%d\n",ans);
}

void readda_() {
	T=read_();
	while(T--) {
		clean_();
		scanf("%s",s);
		d=read_();
		n=strlen(s);
		for(int i=0;i<n;++i) {
			a[i+1]=s[i]-'0';
			++cnt[a[i+1]];
	    }
	    DP_();
	}
}

int main() {
	freopen("a.txt","r",stdin);
	readda_();
	return 0;
} 
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!