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

posted @ 2019-04-01 10:32  章鱼那个哥  阅读(177)  评论(0编辑  收藏  举报