D. Perfect Groups(思维好题)

题意:

一道挺不错的题目。定义一个数组的答案:将这个数组分成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必然是平方数 ab==d2,bc==e2,ac b==de,ac 这个说明,只要两两有关系的数字,都可以加入到同一个组,关系是可以传递的。因此我们可以先预处理出对于每个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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值