(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;
}