[CF980D] Perfect Groups 题解

发布时间 2023-12-13 22:58:00作者: MoyouSayuki

[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;
}