题意:给一个序列,对于每一个连续的区间,区间内的数至少分成几个组,使得每个组内的数任意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;
}
原文:https://www.cnblogs.com/Serene-shixinyi/p/9095228.html