abc392题解
log
25-2-26 新增了 G 题的题解
ABC
略(C其实就是阅读理解)
D
题目大意
在 \(n\) 个骰子中指定两个骰子,问这两个骰子骰到同一个数的概率最大是多少
解题思路
直接暴力就好了:枚举每一个数,对每个数枚举每一对骰子。
CODE
void solve()
{
int n = 0;
std::cin >> n;
std::vector d(n, std::vector(M + 1, 0));
for (int i = 0; i < n; i++) {
std::cin >> d[i][0];
for (int j = 0; j < d[i][0]; j++) {
int a = 0;
std::cin >> a;
d[i][a]++;
}
}
std::vector cnt(n, std::vector(n, 0ll));
for (int k = 1; k <= M; k++) {
for (int i = 0; i < n; i++) {
if (d[i][k] == 0) {
continue;
}
for (int j = i + 1; j < n; j++) {
if (d[j][k] == 0) {
continue;
}
cnt[i][j] += 1ll * d[i][k] * d[j][k];
}
}
}
double ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
ans = std::max(ans, 1.0 * cnt[i][j] / (1ll * d[i][0] * d[j][0]));
}
}
std::cout << std::fixed << std::setprecision(9) << ans << '\n';
}
E
题目大意
在一个图中修改最少的边,使得整张图连通。每条边只能被修改一次,而且只能修改其中一个端点。保证能够通过修改边的端点让整张图连通。
解题思路
用并查集记录连通块。在读入边时,记录下对连通性没有影响的边(即两端点在同一个连通块里的边),这些边就是我们可以拿来做修改的边。
当我们知道图中一共有多少个连通块时,我们就知道了至少要修改都少条边(把连通块看成点就好了)。此后我们在从先前记录下的边中去边,每条边都可以修改成连接两个连通块的边。
CODE
void solve()
{
int n = 0, m = 0;
std::cin >> n >> m;
for (int i = 1; i <= n; i++) {
fa[i] = i;
}
std::vector<std::array<int, 2>> e;
for (int i = 1; i <= m; i++) {
int u = 0, v = 0;
std::cin >> u >> v;
if (not merge(u, v)) {
e.push_back({ i, u });
}
}
std::set<int> root;
for (int i = 1; i <= n; i++) {
if (fa[i] == i) {
root.insert(i);
}
}
std::cout << root.size() - 1 << '\n';
for (auto &[idx, v] : e) {
if (root.size() == 1) {
break;
}
root.erase(get(v));
std::cout << idx << ' ' << v << ' ' << (*root.begin()) << '\n';
merge((*root.begin()), v);
}
return;
}
F
题目大意
给出一个空序列,然后依次插入 1 到 \(n\),并且第 \(i\) 个数要是当前序列的第 \(p_i\) 个数,求最后得到的序列。
解题思路
从小到大考虑的话涉及到的插入太麻烦了。所以我们从大到小考虑删数,维护一个数组 \(T\),\(T_i = 1\) 表示第 \(i\) 个位置上有一个数;\(T_i = 0\) 表示第 \(i\) 个位置上没有数。对于有数的位置,一开始我们并不知道具体是那个数在那里,但是我可以知道每个 1 是第几个 1(区间求和就好了)。根据 1 的排名,我们就可以从从大往小依次确定每个数的位置,在确定一个数的位置后,我们就把该数的位置置为 0 表示我们删去了该数。所以我们需要一个支持单点修改和区间求和的数据结构来代替 \(T\)。
CODE
class BIT {
private:
int n;
std::vector<int> a;
int lowbit(int x) {
return x & -x;
}
int sum(int l) {
int res = 0;
for (int i = l; i >= 1; i -= lowbit(i)) {
res += a[i];
}
return res;
}
public:
BIT(int _n) {
n = _n;
a.assign(n + 1, 0);
}
void add(int pos, int val) {
for (int i = pos; i <= n; i += lowbit(i)) {
a[i] += val;
}
}
int sum(int l, int r) {
return sum(r) - sum(l - 1);
}
};
void solve()
{
int n = 0;
std::cin >> n;
BIT tr(n);
std::vector p(n + 1, 0);
for (int i = 1; i <= n; i++) {
tr.add(i, 1);
std::cin >> p[i];
}
std::vector ans(n + 1, 0);
for (int i = n; i >= 1; i--) {
int l = 1, r = n;
while (l <= r) {
int m = l + r >> 1;
if (tr.sum(1, m) < p[i]) {
l = m + 1;
}
else {
r = m - 1;
}
}
tr.add(l, -1);
ans[l] = i;
}
for (int i = 1; i <= n; i++) {
std::cout << ans[i] << " \n"[i == n];
}
return;
}
G
题目大意
定义三元组 \(<A,B,C>\) 为好的当且仅当这个三元组满足以下条件:
- \(A < B < C\)
- \(B - A = C - B\)
给出 \(N\) 个互不相同且不超过 \(10^6\) 的数,问这些数中共能选出多少不同的好三元组
解题思路
在好三元组中有 \(2B = A + C\),于是对于每个 \(B\) 我们就去找有多少对 \(A,C\) 满足 \(2B = A + C\)。
现在假设我们有一个多项式,这个多项式第 \(i\) 次项的系数为 \(1\) 当且仅当 \(i\) 是题中给出的数中的一个,否则为 \(0\)。于是对这个多项式平方后,\(2B\) 次项的系数就代表有多少对有序的 \(A\) 和 \(C\) 满足 \(2B = A + C\),减去 \(A = C = B\) 的情况再除 2 就得到了以 \(B\) 为最中间那个数的好三元组的数量
于是就可以直接用多项式乘法来做这道题了。(自己写的多项式乘法)
CODE
constexpr int N = 1e5, M = 1e6, Inf = 1e9;
std::vector<int> idx(M << 2);
void fft(int len, std::vector<std::complex<double>> &A, int inv) {
for (int i = 1; i < len; i++) {
if (i < idx[i]) {
std::swap(A[i], A[idx[i]]);
}
}
for (int mid = 1; mid < len; mid <<= 1) {
std::complex<double> unit(cos(M_PI / mid), inv * sin(M_PI / mid));
for (int l = 0; l < len; l += (mid << 1)) {
std::complex<double> w(1.0, 0.0);
for (int i = 0; i < mid; i++, w *= unit) {
std::complex<double> x(A[i | l]), y(w * A[i | l | mid]);
A[i | l] = x + y;
A[i | l | mid] = x - y;
}
}
}
for (int i = 0; inv == -1 && i < len; i++) {
A[i] /= len;
}
return;
}
std::vector<i64> converlution(std::vector<i64> &a, std::vector<i64> &b) {
int la = a.size() - 1, lb = b.size() - 1;
int len = 1;
// 长度是 2 的幂且必须大于最高次数
while (len <= la + lb) {
len <<= 1;
}
for (int l = 1, r = 1, bit = len >> 1; l < len; l <<= 1, bit >>= 1) {
for (int i = 0; i < l; i++) {
idx[r++] = idx[i] | bit;
}
}
std::vector<std::complex<double>> A(len);
for (int i = 0; i < len; i++) {
A[i] = std::complex<double>(i <= la ? a[i] : 0.0, i <= lb ? b[i] : 0.0);
}
fft(len, A, 1);
for (int i = 0; i < len; i++) {
A[i] = A[i] * A[i];
}
fft(len, A, -1);
std::vector res(la + lb + 1, 0ll);
for (int i = 0; i <= la + lb; i++) {
res[i] = i64(A[i].imag() / 2.0 + 0.5);
}
return res;
}
void solve()
{
int n = 0;
std::cin >> n;
std::vector p(M + 1, 0ll);
for (int i = 0; i < n; i++) {
int a = 0;
std::cin >> a;
p[a] = 1;
}
i64 res = 0;
auto q = converlution(p, p);
for (int i = 1; i <= M; i++) {
if (p[i] == 1) {
res += (q[2 * i] - 1) / 2;
}
}
std::cout << res << '\n';
return;
}
浙公网安备 33010602011771号