cf round480D Perfect Groups

题意:给一个序列,对于每一个连续的区间,区间内的数至少分成几个组,使得每个组内的数任意2个相乘是一个完全平方数(包括0)。 输出每个组数的个数。

$n \leq 5000 , |a_i| \leq 10^8$

我们发现,对于一个数$x$,我们把$x$所有成对的相同质因子除去之后得到的数是$f(x)$

那么分到同一个组的所有数的$f(x_i)$相同,0可以被分到任何集合

明显我们可以$n^2$做这道题,枚举一个子序列的右端点,然后从右到左枚举左端点,顺便维护最小组数。

对于加入一个数$x$,我们需要知道当前区间里面的数是否有和$x$相同的,如果有相同的,就加入那个集合,否则就新开一个集合

所以对于每一个点,我们预处理下一个和它相同的数的位置

当然0的情况不同,随便加到任意一个集合就可以了

//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<set>
using namespace std;
#define ll long long
#define db double
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i>=(b);--i)
const int maxn=5000+7,M=120;
ll n,a[maxn],nxt[maxn],ans[maxn];
set<ll> prime;
set<ll>::iterator it;

char cc;ll ff;
template<typename T>void read(T& aa) {
	aa=0;ff=1; cc=getchar();
	while(cc!='-'&&(cc<'0'||cc>'9')) cc=getchar();
	if(cc=='-') ff=-1,cc=getchar();
	while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
	aa*=ff;
}

ll qp(ll x,ll k,ll mod) {
	ll rs=1;
	while(k) {
		if(k&1) rs=rs*x%mod;
		k>>=1; x=x*x%mod;
	}
	return rs;
}

ll gcd(ll x,ll y) {return y==0? x:gcd(y,x%y);}

bool isprime(ll n) {
	if(n==2||n==3||n==5||n==7) return 1;
	if(n<2||(n%6!=1&&n%6!=5)) return 0;
	ll m=n-1,t=0,x,y;
	while((m&1)==0) t++,m>>=1;
	For(qaq,1,20) {
		x=rand()%(n-2)+2;
		x=qp(x,m,n);
		For(i,1,t) {
			y=x*x%n;
			if(y==1&&x!=1&&x!=n-1) return 0;
			x=y;
		}
		if(x!=1) return 0;
	}
	return 1;
}

ll prho(ll n,ll m) {
	ll x=rand()%(n-1)+1,y=0;
	for(ll i=1,k=1,d;y!=x;++i) {
		if(i==k) {y=x;k<<=1;}
		x=(x*x+m)%n;
		d=gcd((y-x+n)%n,n);
		if(d>1&&d<n) return d;
	}
	return n;
}

void find(ll n,ll m) {
	if(isprime(n)) {
		it=prime.find(n);
		if(it==prime.end()) prime.insert(n);
		else prime.erase(it);
		return;
	}
	ll p=n; while(p>=n) p=prho(n,m--);
	find(p,M); find(n/p,M);
}

ll get_num(ll n) {
//	cerr<<"get_num("<<n<<")\n";
	if(n==1||n==0) return n;
	find(n,M);
	ll rs=1;
	while(!prime.empty()) {
		it=prime.begin();
		rs*=*it;
		prime.erase(it);
	}
	return rs;
}

int main() {
	read(n); ll x;
	For(i,1,n) {
//		cerr<<"get a["<<i<<"]:";
		read(x);
		a[i]=get_num(abs(x));
		if(x<0) a[i]=-a[i];
	}
	For(i,1,n) {
		nxt[i]=n+1;
		For(j,i+1,n) if(a[j]==a[i]) {nxt[i]=j;break;}
	}
	int now;
	For(i,1,n) {
		now=0;
		Rep(j,i,1) {
			if(a[j]&&nxt[j]>i) ++now;
			++ans[now];
		}
	}
	ans[1]+=ans[0];
	For(i,1,n) printf("%lld ",ans[i]);
	printf("\n");
	return 0;
}

  

posted @ 2018-05-27 09:51  shixinyi  阅读(195)  评论(0编辑  收藏  举报