CF980D Perfect Groups
题意:
将一个串划分为多个子集(不要求连续),要求同一子集内两任意元素的积为平方数
定义一个串的答案为所需的最少子集个数
一个长度为 n 的串有 n(n+1)2\frac{n(n+1)}{2}2n(n+1)个非空子串,求答案为 1,2,3,⋯,n1,2,3,\cdots ,n1,2,3,⋯,n 的非空子串个数
题解:
这个不应该是紫题。。
先给结论:
如果a,b,c∈N+a,b,c∈N^+a,b,c∈N+,ab=n2ab=n^2ab=n2,bc=m2bc=m^2bc=m2,那么有ac=k2ac=k^2ac=k2。n,m,k∈N+n,m,k∈N^+n,m,k∈N+
你可以理解成有传递性
证明可以用唯一分解定理:
那么说明我们可以将这些平方数用并查集维护在一个集合里,然后n2n^2n2枚举所有子串暴力统计
代码:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
int n,x[5001],fa[5001],ans[5001],num[5001];
int find(int x)
{if(fa[x]==x)return x;return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{fa[find(x)]=find(y);
}
signed main()
{scanf("%lld",&n);for(int i=1;i<=n;i++){scanf("%lld",&x[i]);fa[i]=i;}for(int i=1;i<=n;i++)for(int j=1;j<i;j++)if(x[i]*x[j]>0){int tmp=(int)sqrt(x[i]*x[j]);if(tmp*tmp==x[i]*x[j])merge(i,j);}for(int i=1;i<=n;i++){int tot=0;memset(num,0,sizeof(num));for(int j=i;j<=n;j++)if(x[j]==0)ans[max(1ll,tot)]++;else{if(!num[find(j)]){num[find(j)]=1;tot++;}ans[tot]++;}}for(int i=1;i<=n;i++)printf("%lld ",ans[i]);
}