题意:
一道挺不错的题目。定义一个数组的答案:将这个数组分成k组,每组里面任意一对数字乘积都是平方数,k要最小。题目给出一个n个长度的大小,要你求每个子数组的答案,最后输出答案为1.,2,3…n的子数组各有多少个。
思路:
n最大是5000,子数组个数是n*(n+1)/2,n平方也是没问题的,因此我们只要考虑,如果O(1)的处理 一个子数组。先证明一个东西,假设
a
∗
b
=
=
d
2
,
b
∗
c
=
=
e
2
,
a
c
∗
b
=
=
d
∗
e
,
那
么
a
∗
c
必
然
是
平
方
数
a*b==d^{2},b*c==e^{2},\sqrt{ac}*b==d*e,那么a*c必然是平方数
a∗b==d2,b∗c==e2,ac∗b==d∗e,那么a∗c必然是平方数 这个说明,只要两两有关系的数字,都可以加入到同一个组,关系是可以传递的。因此我们可以先预处理出对于每个j,在i<j中,满足ai*aj是平方数字最大的那个i,mx[j]=i。那么枚举起点l,假设当前处理到r,对于**[l,r+1]**这个子数组,那么我们只要知道mx[r+1]是否比l大,如果比他大那么当前这个数肯定能加入一个已经存在的组,否则不能。
这边有一个坑点就是会有0,要处理一下,0只要不是起点的话,可以加入到任何一个组。如果左端点是0的话可以让某个没有组可以加入的数字加入。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
typedef vector<int>vi;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define fi first
#define se second
#define de(x) cout<<#x<<"="<<x<<endl;
#define dd(x) cout<<#x<<"="<<x<<" " ;
#define pb(x) push_back(x)
#define lowbit(a) ((a)&-(a))
#define per(i,a,b) for(int i=(b)-1;i>=(a);--i)
const int N=2e5+5;
ll a[N],ans[N],mx[N];
int main()
{
int n;
scanf("%d",&n);
rep(i,1,n+1)
scanf("%I64d",&a[i]);
rep(i,1,n+1){
rep(j,i+1,n+1){
ll res=a[i]*a[j];
if(res<=0)continue;
ll t=sqrt(res);
if(t*t==res){
mx[j]=i;
}
}
}
rep(i,1,n+1){
int cnt=0,zero=0;
rep(j,i,n+1){
cnt++;
if(a[j]==0)zero++;
if(mx[j]>=i)
cnt--;
else if(zero&&j!=i)cnt--,zero--;
ans[cnt]++;
}
}
rep(i,1,n+1)
printf("%I64d ",ans[i]);
return 0;
}