题解: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;
}