[CF980D] Perfect Groups 题解
思路
第一个观察就很难观察到:
\[ab = x^2, bc = y^2\Longrightarrow \exist z, ac = z^2(a, b, c \ne 0) \]证明:
两个条件式相乘得到:
\[ab^2c = x^2y^2\\ ac = \dfrac{x^2 y^2}{b^2}(b\ne 0)\\ \because b | x^2, b | y^2\\ \therefore b^2| x^2y^2 \]而由于算术基本定理,右式的分子每一个指数都是偶数,分母每一个都是偶数,相减所得到的每个指数也都是偶数,所以是完全平方数。
所以发现这个性质有传递性,可以考虑用并查集把 \(n\) 个数划分为若干个等价类,区间查询就是这个区间里面有多少个等价类。
注意到证明没有涵盖 0 的情况,事实上,0 乘以任何数都是完全平方数,所以如果一个区间里面有 0,无视它即可。
// Problem: Perfect Groups
// Contest: Luogu
// Author: Moyou
// Copyright (c) 2023 Moyou All rights reserved.
// Date: 2023-12-13 22:29:19
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int n, a[N];
struct DSU {
int fa[N], rk[N], top;
DSU (int n) {
for(int i = 1; i <= n; i ++)
fa[i] = i, rk[i] = 1;
}
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int a, int b) {
int x = find(a), y = find(b);
if(x == y) return ;
if(rk[x] > rk[y]) swap(x, y);
fa[x] = y, rk[y] += (rk[x] == rk[y]);
}
int count(int n) {
int cnt = 0;
for(int i = 1; i <= n; i ++)
if(fa[i] == i) cnt ++;
return cnt;
}
bool same(int a, int b) {return find(a) == find(b); };
};
bool check(ll x) {
ll sq = sqrt(x);
return sq * sq == x;
}
bool st[N];
ll ans[N];
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
DSU dsu(n);
for(int i = 1; i <= n; i ++) {
for(int j = i + 1; j <= n; j ++)
if(a[i] && a[j] && check(1ll * a[i] * a[j]))
dsu.merge(i, j);
}
for(int i = 1, cnt; i <= n; i ++) {
cnt = 0;
for(int j = i; j <= n; j ++) {
if(a[j] && st[dsu.find(j)] == 0) cnt ++, st[dsu.find(j)] = 1; // ignore 0
ans[max(1, cnt)] ++;
}
for(int i = 1, cnt; i <= n; i ++) st[i] = 0;
}
for(int i = 1; i <= n; i ++)
cout << ans[i] << ' ';
return 0;
}
标签:Perfect,Groups,int,题解,CF980D,rk
From: https://www.cnblogs.com/MoyouSayuki/p/17900134.html