传送门:>Here<
题目大意:先抛出了一个问题——“已知一个序列,将此序列中的元素划分成几组(不需要连续)使得每一组中的任意两个数的乘积都是完全平方数。特殊的,一个数可以为一组。先要求最少分几组。”在这个问题的基础上,给出一个长度为n的序列$a_i$,该序列有(frac{n(n+1)}{2})个子串,求每个子串对于上面这个问题最少划分几次。并分别统计最少划分k次的子串有几个。$(n leq 5000, |a_i| leq 10^8)$
解题思路
两个数的乘积为完全平方数,当且仅当两个数都为完全平方数,或者两个数相等。我们考虑放宽一下要求,如果只要求两个数相等,那么题目就变成求区间颜色个数的经典问题了。我们发现,如果我们将每个数的完全平方因子除去,那么所有完全平方数都变成1了,然而并不会影响答案。这样就只剩下两数相等的条件了。
求解所有区间的颜色个数和——常规做法是只让首次出现的颜色产生贡献。这需要我们统计每个数之前出现的相同数的位置。
关于除掉完全平方因子,注意要从大到小除。
Code
/*By QiXingzhi*/ #include <cstdio> #include <cmath> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; const int N = 10010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w; } int n,m; int a[N],ans[N],f[N]; inline int GetNotSquare(int x){ int k = ceil(sqrt(abs(x))); for(int i = 2; i <= k; ++i){ while(x % (i*i) == 0){ x /= i*i; } } return x; } int main(){ n = r; for(int i = 1; i <= n; ++i){ a[i] = r; a[i] = GetNotSquare(a[i]); } f[1] = -1; for(int i = 2; i <= n; ++i){ f[i] = -1; for(int j = i-1; j >= 1; --j){ if(a[i] == a[j]){ f[i] = j; break; } } } for(int i = 1; i <= n; ++i){ int num = 0; for(int j = i; j <= n; ++j){ if(f[j] < i && a[j] != 0){ ++num; } if(num == 0){ ++ans[1]; } else ++ans[num]; } } for(int i = 1; i <= n; ++i) printf("%d ",ans[i]); return 0; }