http://codeforces.com/contest/980/problem/D
大致题意,设F(S)表示在集合S中把元素划分成若干组,使得每组内元素两两相乘的结果的都是完全平方数的最小划分组数
对于给定的序列A,对于每一个k = 1..n,分别求出在A的所有子串[l, r]中有多少满足F(A[l..r]) = k
n <= 5000, abs(A[i]) <= 10^9
先考虑如何求出F(S),对于一个满足要求的组,可以发现对组内元素分解质因数后,每一个质数出现次数的奇偶性相同;
证明 : 如果两数相乘是平方数,设这个数为p1^k1*p2^k2*..*pn*kn,必然有所有k % 2 = 0,当且仅当只有奇偶性相同的两个数相加才能变成偶数,所以每一个分解质因数后每一个质数出现次数的奇偶性相同
通过这个性质,就可以推导出本题的很多做法:
做法1:考虑出现次数为偶数的质因子本身就可以构成平方数,所以导致分组不合法的只可能是出现次数为奇数的质因子,考虑对于序列中所有数分解质因数,把所有出现次数为奇数的质因子放进一个vector中
对于每一个数产生的vector,用map来给他们染色,相同的vector对应的数染成一个颜色,如果说一些数对应的颜色相同,那么说明所有数的奇数质因子在两两相乘的时候都可以消去,即可以把这些数分成一组,那么问题就转变为求区间内不同的颜色数,直接扫一遍即可
做法2:还是考虑偶数因子是没有用的,实际上只需要关心每一个质因子的出现次数在模2意义下的奇偶性,所以大可以把所有数除去他们的平方因子,离散后做一遍区间数颜色即可
做法3:由做法1可以推导出,对于合法的组内的元素,他们出现次数为奇数的质因子都是相同的,那么如果说A * B为完全平方数, B * C 为完全平方数,说明A,B的奇数质因子集合相同,BC的奇数质因子集合相同,所以AC的奇数质因子集合也相同
。所以当满足AB是完全平方数,BC是完全平方数时,AC也是完全平方数,这个东西具有传递性,考虑对于每一个数A[i],只需要找到其前面最后一个能和他合并的数pre[i],对于每一个区间,答案是满足pre[i] < 左端点的i的数量
对于上述所有做法,都要记得特判0!!!
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*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; }