1. 程式人生 > >P3940 分組(貪心+並查集)

P3940 分組(貪心+並查集)

因為字典序最小。

組越少 + 越前面的組size越小!

考慮資料範圍131072!好巧啊!131072 + 131072 = 262144 = 512 * 512.也就是說任意兩個元素之和小於等於512的平方。。。。

那麼我們對於一個元素i可以從512列舉,表示它和另一個元素j之和是某數的平方。

k == 1;

因為組內元素,必須有序

倒著貪心,如果當前元素i能放進上個組(上個組沒有和它之和為平方數)則放.不能放新開一個組,

不能放的那個則是兩個組的分界線。

怎麼找有沒有和元素i之和為平方數的數?

從512倒著列舉,假設列舉的數為k ,如果k*k - i在上一組就必須新開一個組。

k == 2;

也是倒著貪心,只不過當前元素能不能放進上個組的條件變了。

不能出現當前元素和上個組裡的元素i矛盾,同時也和上個組裡的元素j矛盾,而i和j也矛盾。這種情況必須新開組。

//當前元素和元素i不能在同一個小團體,前元素和元素j不能在同一個小團體,元素j和元素i不能在同一個小團體,一個組最多2個小團體。

怎麼表示這種矛盾關係呢??是不是和noip關押罪犯很像啊。

如果當前元素和元素i矛盾,就把當前元素的位置和i的位置+n,並在一起(i 的位置+ n表示i的敵人所在的位置,這樣表示i的敵人和當前元素在同一個小團體).

當然每組的元素可能重複,vector記錄元素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;
}