【XSY2557】【2017国家集训队】Colorful Balls(数学+联通块)

\(Description\)

\(Snuke\)放了\(N\)个一排彩色的球.从左起第\(i\)个球的颜色是\(c_{i}\)重量是\(w_{i}\)

她可以通过执行两种操作对这些球重新排序

操作\(1\):选择两个相同颜色的球,假如他们的重量和小于等于\(X\),交换两个球的位置

操作\(2\):选择两个不同颜色的球,假如他们的重量和小于等于\(Y\),交换两个球的位置

求我们总共可以得到多少种 不同的颜色序列?对答案取\(10^9+7\)的模


\(Input\)

\(N\) \(X\) \(Y\)

\(c_{1}\) \(w_{1}\)

\(c_{N}\) \(w_{N}\)


\(Output\)

输出答案。


\(Sample\) \(input\) \(1:\)

4 7 3
3 2
4 3
2 1
4 4

\(Sample\) \(input\) \(2:\)

1 1 1
1 1

\(Sample\) \(input\) \(3:\)

21 77 68
16 73
16 99
19 66
2 87
2 16
7 17
10 36
10 68
2 38
10 74
13 55
21 21
3 7
12 41
13 88
18 6
2 12
13 87
1 9
2 27
13 15


\(sample\) \(output\) \(1:\)

2

\(sample\) \(output\) \(2:\)

1

\(sample\) \(output\) \(3:\)

129729600


\(HINT\)

\(1≤N≤2×10^5\)

\(1≤X,Y≤10^9\)

\(1≤c_{i}≤N\)

\(1≤w_{i}≤10^9\)


思路

因为里面的球是可以互相交换的,于是我们可以考虑最后利用排列组合来计算答案。

现在我们就要考虑什么样的情况下哪些球是可以交换的

我们考虑一个情况:如果\(A\)能转变到\(B\)\(B\)能转变到\(C\), 那么\(A\)一定能通过\(B\)\(C\)

于是我们就可以将\(A,B,C\)缩在一个联通块里,最后再用组合计算答案

我们考虑:

如果球\(x\)不是全局最小值,那么它最优就是以全局最小值为中介进行交换

如果球\(x\)是全局最小值,那么它最优则是以全局次小值为中介进行交换

接着我们会有个小小的优化:

如果球\(x\)是可以和同色球最小值\(y\)互换的,那么就可以直接把同色最小值赋值给球\(w[x]\)

我们就可以判断如果满足条件的球,就可以将它加入联通块中

最后用组合数计算就好了:\(C^n_m=\frac{m!}{(m-n)!n!}\)

我们就可以暴力处理出两个数组\(fac[i],inv[i]\)分别表示\(i!\)\(\frac{1}{i!}\)


代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e5+10,mod=1e9+7;;
int c[N],w[N];
int minn[N];
int numcol[N];
int cnt=0;
int n;
int fac[N],inv[N];
int poww(int x,int y)
{
    int sum=1ll;
    while(y)
    {
        if(y&1)sum=(sum*x)%mod;
        x=(x*x)%mod;
        y>>=1;
    }
    return sum;
}
void prec()
{
    fac[0]=1;
    for(int i=1;i<=n;i++)fac[i]=i*fac[i-1]%mod;
    inv[n]=poww(fac[n],mod-2);//费马小定理
    for(int i=n-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y)
{
    return ((fac[y]*inv[y-x])%mod*inv[x])%mod;
}
signed main()
{
    memset(minn,127,sizeof(minn));
    int x,y;
    scanf("%lld %lld %lld",&n,&x,&y);
    for(int i=1;i<=n;i++)    
    {
        scanf("%lld %lld",&c[i],&w[i]);
        minn[c[i]]=min(minn[c[i]],w[i]);//同色最小值
    }
    int fir_min=0,sec_min=0;//全局最小值和全局次小值
    for(int i=1;i<=n;i++)
    {
        if(minn[i]<minn[fir_min])
        {
            sec_min=fir_min;
            fir_min=i;
        }
        else if(minn[i]<minn[sec_min])sec_min=i;
    }
    for(int i=1;i<=n;i++)
    {
        if(w[i]+minn[c[i]]<=x)w[i]=minn[c[i]];
        if((c[i]==fir_min&&minn[sec_min]+w[i]<=y)||(c[i]!=fir_min&&minn[fir_min]+w[i]<=y))
        {
            numcol[c[i]]++;
            cnt++;
        }
    }
	//计算组合数
    prec();
    int ans=1;
    for(int i=1;i<=n;i++)
    {
        if(numcol[i])
        {
            ans=(ans*C(numcol[i],cnt))%mod;
            cnt-=numcol[i];
        }
    }
    printf("%lld",ans);
    return 0;
}
posted @ 2019-09-16 13:53  ShuraEye  阅读(180)  评论(0编辑  收藏  举报