noip2019集训测试赛(二十一)Problem A: Colorful Balls

Problem A: Colorful Balls

Description

Snuke放了N个一排彩色的球.从左起第i个球的颜色是ci重量是wi
她可以通过执行两种操作对这些球重新排序
操作1:选择两个相同颜色的球,假如他们的重量和小于等于X,交换两个球的位置
操作2:选择两个不同颜色的球,假如他们的重量和小于等于Y,交换两个球的位置
求我们总共可以得到多少种 不同的颜色序列?对答案取109+7的模

Input

N X Y
c1 w1
.
.
.
cN wN

Output

输出答案。

Sample Input

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

sample output 1:
2
sample output 2:
1
sample output 3:
129729600

HINT

1N2×105

1X,Y109
1ciN
1wi109

分析

首先,如果球A可以和球B交换,球B也可以和球C交换,那么肯定可以通过球B将球A与球C交换。

所以只需要将可以交换的任意两个球之间连一条边,找出每个联通块中每种球颜色的个数,因为每个联通块内无论怎么排都行,所以就能用组合数求出当前那个联通块中合法排列的方案数,最后再将所有的联通块的方案数乘起来就能得到答案了。

但这种方法的时间复杂度是O(n2)的。

但我们可以设:

球a是全部球中重量最小的球。

球b是全部球中与球a不同色且重量最小的球。

若找不出球b,则只有一种颜色的球,故只有1种方案

若球a与球b不在同一个联通块内,则不可能出现两种不同颜色的球在同一个联通块内,故只有1种方案

而对于每一个球来说,如果它既不能连到球a,也不能连到球b,那么它不可能再连到其他异色的球。

所以只可能有1个联通块对答案做出贡献。

所以我们只需要求出所有可以连到球a或球b或连到一个与它同色且能连到球a或球b的球即可。

时间复杂度为O(n)

O(n)的时间求组合数详见欧拉定理

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct data{
    long long x,y;
}t[300001];
long long n,s1,s2,k1,k2,f[300001],k[300001],m=1000000007,s[300001],cnt,id[300001],sum,ans=1,col[300001],colmax[300001];
bool d[300001],r[300001];
bool cmp(data a,data b){
    return a.y<b.y;
}
long long get_pow(long long a,long long b){
    long long p=1;
    while(b){
        if(b%2)p=(p*a)%m;
        a=(a*a)%m;
        b=b/2;
    }
    return p;
}
long long c(long long a,long long b){
    return (((k[a]*f[b])%m)*f[a-b])%m;
}
void add(long long a){
    long long p=a;
    while(t[col[p]].y+t[a].y<=s1){
        if(!col[p])break;
        s[t[a].x]++;
        sum++;
        p=col[p];
        r[p]=1;
    }
}
int main(){
    scanf("%lld%lld%lld",&n,&s1,&s2);
    k[0]=1;
    for(long long i=1;i<=n;i++)k[i]=(k[i-1]*i)%m;
    f[n]=get_pow(k[n],m-2);
    for(long long i=n-1;i>1;i--)f[i]=(f[i+1]*(i+1))%m;
    f[0]=1;
    f[1]=1;
    for(long long i=1;i<=n;i++)scanf("%lld%lld",&t[i].x,&t[i].y);
    sort(t+1,t+n+1,cmp);
    for(long long i=1;i<=n;i++){
        col[colmax[t[i].x]]=i;
        colmax[t[i].x]=i;
    }
    k1=1;
    k2=2;
    while(k2<=n&&t[k2].x==t[k1].x)k2++;
    if(k2!=n+1){
        if(t[k1].y+t[k2].y<=s2){
            for(long long i=1;i<=n;i++){
                if((t[i].x!=t[k1].x&&t[k1].y+t[i].y<=s2)||(t[i].x!=t[k2].x&&t[k2].y+t[i].y<=s2)){
                    if(!d[t[i].x]){
                        d[t[i].x]=1;
                        r[i]=1;
                        cnt++;
                        id[cnt]=t[i].x;
                        s[t[i].x]++;
                        sum++;
                        add(i);
                    }else if(!r[i]){
                        s[t[i].x]++;
                        r[i]=1;
                        sum++;
                    }
                }
            }
            for(long long i=1;i<=cnt;i++){
                ans=(ans*c(sum,s[id[i]]))%m;
                sum=sum-s[id[i]];
            }
        }
    }
    printf("%lld\n",ans);
}

 

  

posted @ 2019-09-19 13:14  ez_suyiheng  阅读(232)  评论(0编辑  收藏  举报