noip2019集训测试赛(二十一)Problem A: Colorful Balls
Problem A: Colorful Balls
Description
Snuke放了N个一排彩色的球.从左起第i个球的颜色是ci重量是wi
她可以通过执行两种操作对这些球重新排序
操作1:选择两个相同颜色的球,假如他们的重量和小于等于X,交换两个球的位置
操作2:选择两个不同颜色的球,假如他们的重量和小于等于Y,交换两个球的位置
求我们总共可以得到多少种 不同的颜色序列?对答案取109+7的模
她可以通过执行两种操作对这些球重新排序
操作1:选择两个相同颜色的球,假如他们的重量和小于等于X,交换两个球的位置
操作2:选择两个不同颜色的球,假如他们的重量和小于等于Y,交换两个球的位置
求我们总共可以得到多少种 不同的颜色序列?对答案取109+7的模
Input
N X Y
c1 w1
.
c1 w1
.
.
.
cN wN
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
1≤N≤2×105
1≤X,Y≤109
1≤ci≤N
1≤wi≤109
分析
首先,如果球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); }