因为字典序最小。
组越少 + 越前面的组size越小!
考虑数据范围131072!好巧啊!131072 + 131072 = 262144 = 512 * 512.也就是说任意两种颜色之和小于等于512的平方。。。。
那么我们对于一个元素i可以从512倒着枚举z(这样可以剪掉平方比i小的,省时),表示它和另一个元素j之和是z的平方。
对于k == 1;
因为组内元素,必须有序(每个小组都是序列上连续的一段)
倒着贪心,如果当前元素i能放进上个组(上个组没有和它之和为平方数)则放.不能放新开一个组,
不能放的话,当前元素则是两个组的分界线。
怎么找有没有和元素i之和为平方数的数?
从512倒着枚举,假设枚举的数为z ,如果z*z - i在上一组就必须新开一个组。
k == 2;
也是倒着贪心,只不过当前元素能不能放进上个组的条件变了。
不能出现当前元素和上个组里的元素i矛盾,同时也和上个组里的元素j矛盾,而i和j也矛盾这种情况。出现则必须新开组。
//当前元素和元素i不能在同一个小团体,前元素和元素j不能在同一个小团体,元素j和元素i不能在同一个小团体,一个组最多2个小团体。
怎么表示这种矛盾关系呢??是不是和noip关押罪犯很像啊。用并查集表示敌对关系。
如果当前元素和元素i矛盾,就把当前元素的位置和i的位置+n,并在一起(i 的位置+ n表示i的敌人所在的位置(当然是虚构的,所以每个位值,其实需要两个位置,另一个表示它的敌人所在位置),这样表示i的敌人和当前元素在同一个小团体).
如果当前元素和元素i已经有共同的祖先了,显然当前元素必须在新开的组里面。
当然每组的元素可能重复,vector[i]记录元素i所对应的出现的所有位置。
#include<bits/stdc++.h>
using namespace std;
vector<int>he[300000];
int n,k,a[150000],an[159999],fa[300000], vis[159999],ans = 1;
void read(int &x)
{
x = 0; int f = 0; char c = getchar();
while(c < '0' || c > '9')
{
if(c == '-') f = 1; c = getchar();
}
while(c >= '0' && c <= '9')
{
x = x * 10 + c - '0'; c = getchar();
}
if(f) x = -x;
}
int find(int x)
{
if(fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void unio(int x,int y)
{
int r1 = find(x); int r2 = find(y);
fa[r1] = r2;
}
void solve1()
{
for(int i = n; i >= 1; i--)
{
int ha = 0;
for(int j = 512; j >= 1; j--)
{
if(j*j < a[i]) break;
if(vis[j * j - a[i]] == ans) {ha = 1; break;}
}
if(ha) an[++ans] = i;
vis[a[i]]= ans;
}
printf("%d\n",ans);
for(int i = ans; i > 1; i--)
printf("%d ",an[i]);
}
void solve2()
{
for(int i = 2*n; i >= 1; i--) fa[i] = i;
for(int i = n; i >= 1; i--)
{
int ha = 0;
for(int j = 512; j >= 1 && ha == 0; j--)
{
if(j*j < a[i]) break;
if(vis[j * j - a[i]] == ans)
for(int k = 0; k < he[j * j -a[i]].size(); k++)
{
if(find(i) == find(he[j * j -a[i]][k]))
{ha = 1;break;}
unio(i+n,he[j * j -a[i]][k]);unio(i,he[j * j -a[i]][k]+n);
}
}
if(ha) { an[++ans] = i; }
if(vis[a[i]] != ans) {
vis[a[i]] = ans; he[a[i]].clear();
}
he[a[i]].push_back(i);
}
printf("%d\n",ans);
for(int i = ans; i > 1; i--)
printf("%d ",an[i]);
}
int main()
{
read(n);read(k);
for(int i = 1; i <= n ;i ++) read(a[i]);
if(k == 1)solve1();
else solve2();
return 0;
}