[??记录]AT2364 [AGC012D] Colorful Balls
command_block
2021-02-28 19:35:48
**题意** : $n$ 个球排成一排,第 $i$ 个球有颜色 $c_i$ 和重量 $w_i$。
可以执行下列两种操作 :
- 选择两个颜色相同,且重量之和不超过 $x$ 的球,交换他们的位置。
- 选择两个颜色不同,且重量之和不超过 $y$ 的球,交换他们的位置。
求可以得到多少种不同的**颜色**序列。答案对 $10^9+7$ 取模。
$n\leq 2\times 10^5,\ w,x,y\leq 10^9$ ,时限$\texttt{2s}$。
------------
对于两个能够交换的球,连一条无向边。
对于一个联通块内的球,可以随便交换顺序,最终方案数即为颜色的多重排列。
这能得到一个显然的 $O(n^2)$ 做法。
考虑简化连边。
考虑最终形成联通块的形态,不难发现是一个大多色块和若干(没有贡献)的单色块。
(若有两个多色块,则必然能合成一个)
大多色块是由若干颜色的(按重量排序的)前缀组成的。
于是,最轻的球一定在大多色块中。
对于同色的球,只需要考虑最轻的球连出的边。
对于异色球,只需要考虑全局最轻的球连出的边,以及全局最轻球所在颜色能连出的最容易的边(即除掉这个颜色后的最轻球)。
这样,所需的边就是 $O(n)$ 的了。
适当地思考性质可以简化代码实现。
```cpp
#include<algorithm>
#include<cstdio>
#define ll long long
#define MaxN 200500
using namespace std;
const int mod=1000000007;
ll powM(ll a,int t=mod-2){
ll ret=1;
while(t){
if (t&1)ret=ret*a%mod;
a=a*a%mod;t>>=1;
}return ret;
}
ll fac[MaxN],ifac[MaxN];
void Init(int n)
{
fac[0]=1;
for (int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%mod;
ifac[n]=powM(fac[n]);
for (int i=n;i;i--)
ifac[i-1]=ifac[i]*i%mod;
}
int n,X,Y,c[MaxN],w[MaxN],pc[MaxN],o[MaxN];
int main()
{
scanf("%d%d%d",&n,&X,&Y);
Init(n);
w[0]=mod;
for (int i=1;i<=n;i++){
scanf("%d%d",&c[i],&w[i]);
if (w[pc[c[i]]]>w[i])pc[c[i]]=i;
}
int p1=0,p2=0;
for (int i=1;i<=n;i++){
if (w[pc[i]]<w[p1]){
p2=p1;p1=pc[i];
}else if (w[pc[i]]<w[p2])
p2=pc[i];
}
if (w[p1]+w[p2]>Y){puts("1");return 0;}
//不同颜色之间无边,则方案数为 1
int m=0;
for (int i=1;i<=n;i++)
if (w[c[i]==c[p1] ? p2 : p1]+w[i]<=Y
||(w[i]+w[pc[c[i]]]<=X&&w[pc[c[i]]]+w[p1]<=Y))
{m++;o[c[i]]++;}
//否则,任意有向外出边的球都在大多色块中
ll ans=fac[m];
for (int i=1;i<=n;i++)
ans=ans*ifac[o[i]]%mod;
printf("%lld",ans);
return 0;
}
```