题解 AT2364 【Colorful Balls】
标签: Luogu 都咩有标签我怎么编的出.
Part 1
我们发现如果球
枚举两个球, 如果满足交换条件就连一条边, 则一个联通块内的所有点可以随意决定相对顺序. 那么我们该如何计算答案呢, 容易发现不同连通块的方案数是独立(分步)的, 所以我们对每个联通快分别求答案然后相乘即可. 易知一个联通快的方案数为
Par2
上述做法还不够优秀, 考虑优化.
发现一个联通块内有许多边是无用的(断开后不影响联通性), 考虑优化这个部分.
发现对于同色相连的边, 只用连重量最小的球和其他球之间的边.
对于异色相连的边, 只用连 最小重量最小的颜色重量最小的球 与 其它颜色的重量最小的球 之间的边 和 最小重量最小的颜色重量非最小的球 与 最小重量次小颜色的重量最小的球之间 的边就好了(有点绕口, 加油! *<|:-) ).
另外, 根据 Part1 可知: 一个联通块内至少有两种颜色对答案才有贡献, 也即如果一种颜色无法与 最小重量最小的颜色 连边, 那么对答案一定没有贡献, 也即只有一个联通块对答案有贡献. 所以图就不用建了, 代码实现非常简单.
复杂度
(由于很简单变量名瞎取).
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
int read();
vector<int> c[200005];
int w[200005];
int fsp(int bs, int p) {
int rt = 1;
while (p) {
if (p & 1) rt = 1ll * rt * bs % mod;
bs = 1ll * bs * bs % mod, p >>= 1;
}
return rt;
}
int fac[200005], caf[200005];
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = 1ll * i * fac[i - 1] % mod;
caf[n] = fsp(fac[n], mod - 2);
for (int i = n; i >= 1; --i) caf[i - 1] = 1ll * i * caf[i] % mod;
}
int main() {
int n = read(), X = read(), Y = read();
init(n);
for (int i = 1, tc, tw; i <= n; ++i) {
tc = read(), tw = read();
if (!c[tc].size()) w[tc] = tw;
c[tc].push_back(tw), w[tc] = min(tw, w[tc]);
}
int mc1 = 0, mc2 = 0;
for (int i = 1; i <= n; ++i)
if (c[i].size() && (!mc1 || w[i] < w[mc1])) mc1 = i;
for (int i = 1; i <= n; ++i)
if (c[i].size() && i != mc1 && (!mc2 || w[i] < w[mc2])) mc2 = i;
if (!mc2 || w[mc1] + w[mc2] > Y) return puts("1"), 0;
int res1 = 0, res2 = 1;
for (int i = 1, cnt; i <= n; ++i) {
if (!c[i].size() || w[i] + w[mc1] > Y) continue;
cnt = 0;
for (vector<int>::iterator j = c[i].begin(); j != c[i].end(); ++j) {
cnt += ((*j) + w[mc1 - i ? mc1 : mc2] <= Y || (*j) + w[i] <= X);
}
res1 += cnt, res2 = 1ll * caf[cnt] * res2 % mod;
}
printf("%d\n", 1ll * fac[res1] * res2 % mod);
return 0;
}
int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}