zoukankan      html  css  js  c++  java
  • SSY的队列 hash+记忆化

    题目描述

    (SSY) 是班集体育委员,总喜欢把班级同学排成各种奇怪的队形,现在班级里有 (N) 个身高互不相同的同学,请你求出这 (N) 个人的所有排列中任意两个相邻同学的身高差均不为给定整数M的倍数的排列总数。

    输入格式

    共三行:

    第一行为 (N)

    第二行为 (N) 个不同的整数

    第三行为 (M)

    输出格式

    一行,为符合条件的排列总数(答案对 (1234567891) 取余数)。

    样例

    样例输入1

    3
    -1 0 1
    2

    样例输出1

    2

    样例输入2

    4
    1 2 3 4
    3

    样例输出2

    12

    数据范围与提示

    (20\%) 的数据:(N<=11)

    (70\%) 的数据:(N<=15)

    (100\%) 的数据:(N<=30,M<=1000)

    分析

    对于一个数,它原来的值和它对 (m) 取模之后的值在这道题中意义是相同的

    所以一共只会有 (m) 种数

    我们记录一下每一种数有多少个,然后把个数存进栈里

    我们会发现,方案数仅与当前的数和剩下个数为 (x) 的数有几种有关

    比如当 (m=5) 时,(3 2 2)(3 1 1) 的结果是完全一样的

    可以设 (f[i][j][k][...]) 为当前选的是第 (i) 种数,剩下个数为 (1) 的数有 (j) 种,剩下个数为 (2) 的数有 (k) 种 ... 的方案数

    数组开不下,所以我们可以用哈希的思想把状态压成一个

    对于递归的每一层,开一个 (map) 记录一下即可

    要注意的是最后要乘上每一种数个数的阶乘,因为同一种数可以任意交换位置

    代码

    #include<cstdio>
    #include<map>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    #define ull unsigned long long
    const int maxn=35;
    const int mod=1234567891;
    const ull bas=233333;
    int a[maxn],sta[maxn],b[maxn],n,m,tp,mmax,jc[maxn];
    bool vis[maxn];
    std::map<ull,int> mp[maxn];
    int dfs(int now,int lat){
    	if(now>n){
    		return 1;
    	}
    	memset(b,0,sizeof(b));
    	for(rg int i=1;i<=tp;i++){
    		if(i!=lat) b[sta[i]]++;
    	}
    	ull nans=sta[0];
    	for(rg int i=0;i<=mmax;i++){
    		nans=nans*bas+b[i];
    	}
    	nans=nans*bas+sta[lat];
    	if(mp[now].find(nans)!=mp[now].end()) return mp[now][nans];
    	rg int mans=0;
    	if(sta[0]>0){
    		sta[0]--;
    		mans=((long long)mans+(long long)dfs(now+1,0))%mod;
    		sta[0]++;
    	}
    	for(rg int i=1;i<=tp;i++){
    		if(i!=lat && sta[i]>0){
    			sta[i]--;
    			mans=((long long)mans+(long long)dfs(now+1,i))%mod;
    			sta[i]++;
    		}
    	}
    	mp[now][nans]=mans;
    	return mans;
    }
    int main(){
    	n=read();
    	for(rg int i=1;i<=n;i++){
    		a[i]=read();
    	}
    	m=read();
    	for(rg int i=1;i<=n;i++){
    		a[i]%=m;
    		if(a[i]<0) a[i]+=m;
    	}
    	rg int ncnt=0;
    	for(rg int i=1;i<=n;i++){
    		if(vis[i]) continue;
    		vis[i]=1;
    		ncnt=0;
    		for(rg int j=i;j<=n;j++){
    			if(a[i]==a[j]){
    				vis[j]=1;
    				ncnt++;
    			}
    		}
    		mmax=std::max(mmax,ncnt);
    		if(ncnt==1) sta[0]++;
    		else sta[++tp]=ncnt;
    	}
    	jc[0]=1;
    	for(rg int i=1;i<=n;i++){
    		jc[i]=1LL*jc[i-1]*i%mod;
    	}
    	rg int ans=1;
    	for(rg int i=0;i<=tp;i++){
    		ans=1LL*ans*jc[sta[i]]%mod;
    	}
    	ans=1LL*ans*dfs(1,0)%mod;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    第二阶段冲刺10
    第二阶段冲刺9
    第二阶段冲刺8
    (转载)关于数组的几个面试题
    关于静态变量
    linux进程地址空间详解(转载)
    单例模式,多种实现方式JAVA
    最佳线程数
    python学习
    svn设置
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14002029.html
Copyright © 2011-2022 走看看