CCA的小球——牛客练习赛78
题目链接:https://ac.nowcoder.com/acm/contest/11168/D
大意
给定 n 个小球,每个小球有颜色,要将它们摆成一行 。
两个方案不同,当且仅当存在某个位置,两种方案摆在这个位置的小球颜色不同。
一个方案合法, 当且仅当不存在任意两个位置相邻的小球颜色相同,求合法方案数对 10^9+7 取模后的值 。
n <= 10^6,0 < 颜色编号 < 2^31,每种颜色出现次数 <= 2
思路
同样一种小球,有相邻和不相邻两种情况
题目求零种小球相邻的方案数。
首先我们能求得至少i种小球相邻的方案,即 C n u m i C_{num}^i Cnumi * (n - i) ! / 2 n u m − i 2^{num-i} 2num−i ,(num为颜色出现两次的小球个数)
那么我们就能使用容斥来去重,至少0种-至少1种+至少2种…
最后!!!答应我一定要好好预处理!
细节操作看代码。代码有点丑表要介意:)
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1000000007;
ll num;
ll ksm(ll a,ll b)
{
ll r=1,base=a;
while(b!=0)
{
if(b%2)
r=r*base%mod;
base=base*base%mod;
b/=2;
}
return r%mod;
}
ll fa[1000007];
ll _2[1000007];
ll inv[1000007];
ll a[1000007];
int main()
{
ios::sync_with_stdio(0);
ll n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
sort(a+1,a+n+1);
for(int i=1;i<n;i++)
{
if(a[i]==a[i+1])
num++;
}
_2[0]=1;
fa[0]=1;
for(ll i=1;i<=1000000;i++)
{
fa[i]=fa[i-1]*i%mod;//预处理阶乘
_2[i]=_2[i-1]*2%mod;//预处理2的倍数
}
inv[1000000]=ksm(fa[1000000],mod-2);
for(ll i=1000000-1;i>=0;i--)
inv[i]=(inv[i+1]*(i+1))%mod;//预处理逆元,可以理解为倒数
ll sum=0,op=-1;
for(int i=0;i<=num;i++)
{
op=-op;
sum=(sum+op*(fa[num]*inv[i]%mod*inv[num-i]%mod*fa[n-i]%mod*ksm(_2[num-i],mod-2)%mod)%mod)%mod;//不建议用组合板子,因为过不了。。。
if(sum<0)
sum+=mod;
}
cout<<(sum%mod+mod)%mod<<endl;
return 0;
}