【题解】 SP5973 SELTEAM - Selecting Teams
\(Description:\)
给一个序列,每个数有一个颜色c,权值w,不同颜色的若w和小于等于B可交换,相同颜色若w和小于等于A可交换,求最后的序列可能的个数,对1e9+7取模。(相同颜色不同权值算同一种)
\(Sample\) \(Input\):
4 7 3
3 2
4 3
2 1
4 4
\(Sample\) \(Output\):
2
简化一下题目,发现其实是先求出最大联通块的大小,设这个联通块大小为s,颜色相同的点的个数为x,那么答案就是\(A_{s}^{s-x}\),A是排列数,那么考虑怎么求出最大联通块的大小,可以计每个颜色的最小权值,在计一个全局最小权值,如果全局最小可以和颜色最小连边,就连边。
一开始先处理出每个颜色的可以和该颜色最小值连边的所有点,更新这个颜色的联通块大小。
但要注意,如果该颜色是全局最小值的颜色的话,不能直接跟全局最小值加起来比较,还要考虑,他和其他次小元素的和是否可以跨颜色连边。
然后把所有最小值尝试和全局最小值连边,更新s。
在更新答案,(注意逆元问题)
然后就差不多结束了_
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define int long long
using namespace std;
int n,m,cnt,Min_all,A,B,ans,ss,num;
const int MAXN=1e6;
const int OO=0x3f3f3f3f;
const int p=1e9+7;
int size[MAXN+10];
int c[MAXN+10],w[MAXN+10];
vector <int> v[MAXN+10];
int fac[MAXN+10],inv[MAXN+10];
int bac[MAXN+10],Min[MAXN+10];
int _bac[MAXN+10];
inline int Inv(int a,int b,int p){
int ret=1;
for(;b;a=(a*a)%p,b>>=1)if(b&1)ret=(ret*a)%p;
return ret;
}
signed main(){
if(fopen("keep.in","r")){
freopen("keep.in","r",stdin);
freopen("keep.out","w",stdout);
}
scanf("%lld%lld%lld",&n,&A,&B);
for(int i=1;i<=n;++i)Min[i]=i;
fac[0]=fac[1]=1;
for(int i=2;i<=n;++i)fac[i]=(fac[i-1]*i)%p;
inv[n]=Inv(fac[n],p-2,p)%p;
inv[0]=inv[1]=1;
for(int i=n-1;i>=2;--i)inv[i]=(inv[i+1]*(i+1))%p;
for(int i=1;i<=n;++i)scanf("%lld%lld",&c[i],&w[i]);
for(int i=1;i<=n;++i){
if(!bac[c[i]])bac[c[i]]=++cnt,_bac[cnt]=c[i];
v[bac[c[i]]].push_back(i);
}
for(int i=1;i<=cnt;++i)for(int j=0;j<(int)v[i].size();++j){
if(j==0)Min[i]=v[i][j];
else
Min[i]=(w[Min[i]]>w[v[i][j]])?v[i][j]:Min[i];
}
for(int i=1;i<=cnt;++i)size[Min[i]]=(int)v[i].size();
Min_all=Min[1];
for(int i=2;i<=cnt;++i)
Min_all=(w[Min_all]>w[Min[i]])?Min[i]:Min_all;
ss=OO;
for(int i=1;i<=cnt;++i){
if(Min_all==Min[i])continue;
ss=min(ss,w[Min[i]]);
}
for(int i=1;i<=cnt;++i)for(int j=0;j<(int)v[i].size();++j){
if(Min[i]!=v[i][j] && w[Min[i]]+w[v[i][j]]<=A)continue;
if(c[Min_all]!=_bac[i] && w[v[i][j]]+w[Min_all]<=B)continue;
if(c[Min_all]==_bac[i] && w[v[i][j]]+ss<=B)continue;
size[Min[i]]--;
}
num=size[Min_all];
for(int i=1;i<=cnt;++i){
if(Min_all==Min[i])continue;
if(w[Min[i]]+w[Min_all]<=B)
num+=size[Min[i]];
}
ans=1;
for(int i=1;i<=cnt;++i){
if(w[Min[i]]+w[Min_all]<=B || Min_all==Min[i])
ans=(ans*inv[size[Min[i]]])%p;
}
ans=(ans*fac[num])%p;
printf("%lld\n",ans);
return 0;
}