(>Codeforcesspace980 D. Perfect Groups<)
题目大意 : 设 (F(S)) 表示在集合(S)中把元素划分成若干组,使得每组内元素两两相乘的结果的都是完全平方数的最小组数
对于长度为(n)的序列 (A) ,对于每一个 (k \, (1 leq k leq n)) ,分别求出在(A)的所有子串中有多少 ([l, r]) 满足 (F(A[l, r]) = k)
$n leq 5000, space |A_{i}| leq 10^9 $
解题思路 :
先考虑如何求出 (F(S)) ,发现对于一个满足要求的组,对组内元素分解质因数后,每一个质因子出现次数的奇偶性相同
证明: 如果两数相乘是平方数,设这个数为 (p_{1}^{k1} imes p_{2}^{k2} imes.. imes p_{n} ^{kn} (p is prime)),必然对于所有 (k) ,满足(k equiv 0 pmod {2})
又因为当且仅当奇偶性相同的两个数相加才能变成偶数,所以对于组内的每一个数分解质因数后质因子出现次数的奇偶性相同
观察发现,出现次数为偶数的质因子出现与否并不影响答案,所以可以直接消去,对于所有出现次数为奇数的质因子,也只需要保留一个即可,这样做等价于将数中的所有平方因子全部消去
将问题回到序列上,发现消去平方因子后如果两个数 (a,b) 能分到一组,当且仅当 (a = b) ,问题就转变为区间不同的数的个数,直接暴力统计即可,注意特判 (0) 可以放到任意组。
/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define N (100005)
#define int ll
struct Point{ int x, id; } e[N];
int a[N], buf[N], Ans[N], n, col, f0;
inline bool cmp(Point A, Point B){ return A.x < B.x; }
inline int change(int x){
int res = x;
for(int i = 2; i * i <= abs(x); i++)
while(res % (i * i) == 0) res /= i * i;
return res;
}
main(){
read(n);
for(int i = 1; i <= n; i++) read(a[i]), a[i] = change(a[i]);
for(int i = 1; i <= n; i++) e[i].x = a[i], e[i].id = i;
sort(e + 1, e + n + 1, cmp);
a[e[1].id] = ++col; if(e[1].x == 0) f0 = 1;
for(int i = 2; i <= n; a[e[i++].id] = col){
if(e[i].x > e[i-1].x) col++;
if(e[i].x == 0) f0 = col;
}
for(int i = 1; i <= n; i++){
int res = 0;
for(int j = i; j <= n; j++){
if(!buf[a[j]] && a[j] != f0) res++;
buf[a[j]]++;
if(!res) Ans[1]++; else Ans[res]++;
}
for(int j = i; j <= n; j++) buf[a[j]]--;
}
for(int i = 1; i <= n; i++) cout << Ans[i] << " ";
return 0;
}