这题乍一看没有什么头绪,有点凌乱。
不妨建一个更加明晰的问题模型:
有n个球,分成k堆,第 i 堆球的颜色都是c[i],
每个球 j 都有一个重量w[j],且标有一个1~n的数字(代表它在序列中的位置)。
每次你能做的操作就是交换某两个球的数字,并且
同一堆的两个球能够交换数字,当且仅当重量和<=X;
不同堆的两个球能够交换数字,当且仅当重量和<=Y。
问最后有多少种本质不同的填数方案(两种方案本质不同当且仅当存在某个数字x所在的堆不同)。
如何计算答案?
我们考虑哪些球之间可以交换数字(可以交换的连一条边)。
我们记第i堆中重量最小的球为
(1)同一堆内(第P堆),如果,那么存在边。
但我们发现,事实上我们只需要保留形如的边就足够了,因为i和j可以通过Min_P互相连通。
(2)不同的两堆(第A堆和第B堆),
枚举第A堆的球i和第B堆的球j,如果,那么存在边。
但我们发现,事实上我们只需要保留形如以及的边就足够了。
因为如果存在,那么也会存在、以及,去掉仍然可以保证i和j连通。
(3)更一般地,考虑若干堆之间的联系。
假设所有堆已经按照递增的顺序排列。
对于形如的边我们也可以去掉,只需要保留。
---------------
对于一个堆i,如果不存在边,那么这个堆显然无法和外界连通,并不会影响答案,可以忽略它了。
否则,这个堆里有两种球x:
(1)不存在边,也不存在边,那这个球就等于被孤立了,可以忽略它了;
(2)存在边或,这个球就可以和Min_1连通。
现在我们发现,除了被忽略的球以外,其它所有球都和Min_1连通,彼此都可以交换数字。
记一共有个球彼此连通,每个堆的球数(不考虑忽略的)为,答案=
#include
#include
#define fs first
#define sc second
#define mp make_pair
#define ll long long
#define org b[1].sc
#define rep(i,j,k) for (i=j;i<=k;i++)
#define down(i,j,k) for (i=j;i>=k;i--)
using namespace std;
const int N=2e5+5,mod=1e9+7;
typedef pair Pair;
Pair a[N],b[N];
int n,bn,col,X,Y,i,sum,ans;
int fac[N],inv[N],cnt[N],flg[N],Min[N],con[N];
int fgm(int a,int n)
{
int ret=1,bsc=a;
for (;n>0;n>>=1,bsc=(ll)bsc*bsc%mod)
if (n&1) ret=(ll)ret*bsc%mod;
return ret;
}
int main()
{
scanf("%d%d%d",&n,&X,&Y);
rep(i,1,n) scanf("%d%d",&a[i].sc,&a[i].fs);
sort(a+1,a+1+n);
rep(i,1,n)
if (!flg[a[i].sc]) b[++bn]=a[i],Min[a[i].sc]=a[i].fs,flg[a[i].sc]=1;
if (bn==1 || b[1].fs+b[2].fs>Y) { printf("1\n"); return 0; }
rep(i,2,bn) {
if (b[i].fs+b[1].fs>Y) break;
con[b[i].sc]=1;
}
rep(i,1,n)
{
col=a[i].sc;
if (col==org) {
if (a[i].fs+Min[col]<=X || a[i].fs+b[2].fs<=Y) cnt[col]++;
}
else
if (con[col]) {
if (a[i].fs+Min[col]<=X || a[i].fs+Min[org]<=Y) cnt[col]++;
}
}
fac[1]=1; rep(i,2,n) fac[i]=(ll)fac[i-1]*i%mod;
inv[n]=fgm(fac[n],mod-2);
down(i,n-1,1) inv[i]=(ll)inv[i+1]*(i+1)%mod;
ans=1;
rep(i,1,n)
if (cnt[i]) sum+=cnt[i],ans=(ll)ans*inv[cnt[i]]%mod;
ans=(ll)ans*fac[sum]%mod;
printf("%d\n",ans);
return 0;
}