题解:ssy的队列

题目链接

题目描述

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

思路

先把题目转化一下:
给一些数和一个模数,这些数模上这个模数结果相同的分为一类
同类数不能相邻,问有多少种排列方式

易知同一类数的效果在序列中是一样的,可以将他们直接视为一种
差异只存在于数的不同大小,在最后乘上一个各个种类数字个数阶乘的乘积就行

\(f[i][k]\) 表示已经遍历到第 \(i\) 位,此时最后一个数字还剩下 \(k\)
可以用 hash 存每种颜色的状态,但是 map 套 vector 确实方便

转移的时候只用遍历每种状态的剩余种类进行转移就好
需要注意的是得钦定一个和 \(k\) 大小相同的种类为最后一个,不对它进行转移

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=33,mod=1234567891;
int n,m,k,tot,a[N],b[N],sum=1;
map<vector<int>,int>f[N][N];
map<int,int>mp;
vector<int>v,t;
signed main()
{
	freopen("ssy.in","r",stdin);
	freopen("ssy.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
		scanf("%lld",&b[i]);
	cin>>m;
	for(int i=1,x;i<=n;i++)
	{
		if(mp.find((b[i]%m+m)%m)==mp.end())
			mp[(b[i]%m+m)%m]=++tot;
		a[mp[(b[i]%m+m)%m]]++;
	}
	sort(a+1,a+tot+1);
	for(int i=1;i<=tot;i++)
		v.push_back(a[i]);
	f[0][0][v]=1;
	for(int i=0;i<n;i++)
		for(int k=0;k<=n;k++)
			for(auto it=f[i][k].begin();it!=f[i][k].end();it++)
			{
				v=it->first;
				for(int s=0,fl=0;s<v.size();s++)
				{
					if(v[s]==0)
						continue;
					if(v[s]==k&&!fl)
					{
						fl=1;
						continue;
					}
					t=v;
					t[s]--;
					sort(t.begin(),t.end());
					f[i+1][v[s]-1][t]+=it->second;
					f[i+1][v[s]-1][t]%=mod;
				}
			}
	for(int i=1;i<=tot;i++)
	{
		v[i]=0;
		for(int j=1;j<=a[i];j++)
			sum=(sum*j)%mod;
	}
	printf("%lld\n",sum*f[n][0][v]%mod);
	return 0;
}
posted @ 2024-05-04 15:51  lxyt-415x  阅读(88)  评论(3编辑  收藏  举报