版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83305375
题目
把n个数按顺序分成若干组。定义矛盾为两个数的和为一个完全平方数。
K=1时,不允许组内存在矛盾。
K=2时,把小组分成两个团体,每个团体内不存在矛盾。
特性
很贪心的一个题。
因为要求分组位置尽量靠前,所以从后往前贪心,每次使当前区间尽量长。
题解K=1
模拟
这个很暴力,直接判断i能否加入当前区间即可。
题解K=2
并查集
有点像关押罪犯那题,两个矛盾的尽量分放两边,这个就可以用并查集维护了。每个点拆成两个点,如果有矛盾,就交叉连边。当拆成的两个点处在一个集合中时,矛盾。
此外还要考虑一个数字重复出现的情况。如果要是它们之和不是完全平方数,那么放在一边就可以了;否则就要放两边了,如果放两边,那么就不允许有任何一个数会与他矛盾。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sqr_inf=131100,N=131100;
const int maxn=131100;
int n,k;
int a[maxn];
int fa[maxn*2];
int findfa(int x)
{
if(fa[x]==x) return x;
return fa[x]=findfa(fa[x]);
}
bool check(int x,int y)//判断x和y(已冲突)能不能分放两边
{
int x1=findfa(x),x2=findfa(x+N);
int y1=findfa(y),y2=findfa(y+N);
if(x1==y1 || x2==y2) return false;
fa[x1]=y2;fa[x2]=y1;
return true;//debug
}
bool issqr[maxn*2];
int m=0,b[maxn];
bool vis[maxn],dvis[maxn];
void work1()
{
for(int i=n,j=n;i>=1;)//debug i--
{
for(;j>=1;j--)
{
for(int k=1;k*k-a[j]<sqr_inf;k++) if(k*k-a[j]>0)
if(vis[k*k-a[j]]) goto fail;
vis[a[j]]=true;
}
fail: if(j==0) break;
b[++m]=j;
for(;i>j;i--) vis[a[i]]=false;
}
}
void work2()
{
for(int i=1;i<=2*N;i++) fa[i]=i;
for(int i=1;i*i<2*sqr_inf;i++) issqr[i*i]=true;//debug <sqr_inf 因为当a[j]=N时,最大有N*2
for(int i=n,j=n;i>=1;)//debug i--
{
for(;j>=1;j--)
if(vis[a[j]])//a[j]出现过,现在是第2次出现
{
if(issqr[a[j]*2])
{
if(dvis[a[j]]) goto fail;//已经出现了两次
for(int k=1;k*k-a[j]<sqr_inf;k++) if (k*k-a[j]>0)
if(vis[k*k-a[j]] && k*k!=a[j]*2) goto fail;//两边都有一个a[j],如果有一个数为k*k-a[j],放那边都不行
dvis[a[j]]=true;//重复出现
}
}
else//第1次出现
{
for(int k=1;k*k-a[j]<sqr_inf;k++) if(k*k-a[j]>0)
if(vis[k*k-a[j]] && ( dvis[a[j]] || dvis[k*k-a[j]] || !check(a[j],k*k-a[j]) )) goto fail;//如果不能放两边,而且各放一边还会冲突,那不行
vis[a[j]]=true;//出现记录
}
fail: if(j==0) break;
b[++m]=j;
for(;i>j;i--) fa[a[i]]=a[i],fa[a[i]+N]=a[i]+N,vis[a[i]]=dvis[a[i]]=false;
fa[a[i]]=a[i];fa[a[i]+N]=a[i]+N;//注意i无法还原到i=j
}
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
if(k==1) work1();
else work2();
printf("%d\n",m+1);
for(int i=m;i>=1;i--) printf("%d ",b[i]);
puts("");
return 0;
}