【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;
}
原文地址:https://www.cnblogs.com/ShuraEye/p/11526958.html