[洛谷P3940]:分组(贪心+并查集)
题目传送门
题目描述
小$C$在了解了她所需要的信息之后,让兔子们调整到了恰当的位置。小$C$准备给兔子们分成若干个小组来喂恰当的胡萝卜给兔子们吃。
此时,$n$只兔子按一定顺序排成一排,第$i$只兔子的颜色是$a_i$。由于顺序已经是被调整好了的,所以每个小组都应当是序列上连续的一段。
在分组前,小$C$发现了一个规律:有些兔子会两两发生矛盾。并且,两只兔子会发生矛盾,当且仅当代表他们的颜色的数值之和为一个正整数的平方。比如,$1$色兔子和$2$色兔子不会发生矛盾,因为$3$不是任何一个正整数的平方;而$1$色兔子却会和$3$色兔子发生矛盾,因为$4=2^2$。
小$C$认为,只要一个小组内的矛盾不要过大就行。因此,小$C$定义了一个小组的矛盾值$k$,表示在这个小组里,至少需要将这个组再一次分成$k$个小团体;每个小团体并不需要是序列上连续的一段,但是需要使得每个小团体内任意两只兔子之间都不会发生矛盾。
小$C$要求,矛盾值最大的小组的矛盾值$k$不超过$K$就可以了。当然,这样的分组方法可能会有很多个;为了使得分组变得更加和谐,小$C$想知道,在保证分组数量最少的情况下,字典序最小的方案是什么。你能帮帮她吗?
字典序最小的方案是指,按顺序排列分组的间隔位置,即所有存在兔子$i$和$i+1$在不同组的位置。
输入格式
输入第1行两个正整数$n,K$。
输入第2行$n$个正整数,第$i$个数表示第$i$只兔子的颜色$a$。
输出格式
输出第$1$行一个正整数$m$,为你至少需要将兔子分为多少个小组。
输出第$2$行$m-1$个从小到大的排列的正整数,第$i$个数$s_i$表示$s_i$和$s_{i+1}$在你的方案里被分到了两个小组。如果$m=1$,那么请输出一个空行。
样例
样例输入1:
5 2
1 3 15 10 6
样例输出1:
2
1
样例输入2:
319 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
样例输出2:
6
7 31 71 127 199
数据范围与提示
样例$1$解释:
如果将五只兔子全部分到同一个小组的话,那么$(1,3)(3,6)(6,10)(10,15)(1,15)$均不能分到同一个小团体;因为最多分成两个小团体,所以为了满足前4对限制,只能分为$\{\{1,6,15\},\{3,10\}\}$,但此时不满足$(1,15)$,所以不存在一种组数为$1$的方案满足全部限制。
如果将五只兔子分为两个小组的话,一种字典序最小的可行的分组方案是$\{1\},\{3,15,10,6\}$,此时第二组内的小团体数量不超过$2$的一种分法是$\{\{3,10\},\{15,6\}\}$。
数据范围:
任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
测试点 $K$ $n$ $a_i,c_j$ 特殊性质$1$ 特殊性质$2$
$1$ $1$ $2$ $\leqslant 131072$ $\surd$ $\times$
$2$ $1$ $4$ $\leqslant 131072$ $\times$ $\times$
$3$ $1$ $16$ $\leqslant 131072$ $\times$ $\times$
$4$ $1$ $256$ $\leqslant 131072$ $\surd$ $\times$
$5$ $1$ $1024$ $\leqslant 131072$ $\surd$ $\times$
$6$ $1$ $1024$ $\leqslant 131072$ $\times$ $\times$
$7$ $1$ $131072$ $\leqslant 2$ $\surd$ $\times$
$8$ $1$ $131072$ $\leqslant 131072$ $\surd$ $\times$
$9$ $1$ $131072$ $\leqslant 131072$ $\times$ $\surd$
$10$ $1$ $131072$ $\leqslant 131072$ $\times$ $\times$
$11$ $2$ $2$ $\leqslant 131072$ $\surd$ $\times$
$12$ $2$ $4$ $\leqslant 131072$ $\times$ $\times$
$13$ $2$ $8$ $\leqslant 131072$ $\times$ $\times$
$14$ $2$ $16$ $\leqslant 131072$ $\times$ $\times$
$15$ $2$ $256$ $\leqslant 131072$ $\surd$ $\times$
$16$ $2$ $256$ $\leqslant 131072$ $\times$ $\surd$
$17$ $2$ $1024$ $\leqslant 2$ $\surd$ $\times$
$18$ $2$ $1024$ $\leqslant 131072$ $\times$ $\times$
$19$ $2$ $4096$ $\leqslant 2$ $\times$ $\times$
$20$ $2$ $4096$ $\leqslant 131072$ $\times$ $\times$
$21$ $2$ $131072$ $\leqslant 2$ $\times$ $\times$
$22$ $2$ $131072$ $\leqslant 131072$ $\times$ $\surd$
$23$ $2$ $131072$ $\leqslant 131072$ $\times$ $\surd$
$24$ $2$ $13102$ $\leqslant 131072$ $\times$ $\times$
$25$ $2$ $131072$ $\leqslant 131072$ $\times$ $\times$
题解
对于$K=1$:
测试点$1$:$n=2$
直接判一下两数值和是否为完全平方数。
是就$puts("2$ \ $n1");$,不是就$puts("1$ \ $n");$。
时间复杂度:$\Theta(1)$。
期望得分:$4$分。
实际得分:$4$分。
总得分:$4$分。
测试点$2$:$n=4$
手动讨论所有情况即可。
时间复杂度:$\Theta(1)$。
期望得分:$4$分。
实际得分:$4$分。
总得分:$8$分。
测试点$3$:$n=16$
暴力搜索,可以得到所有的分组情况,一个一个验证就好了。
时间复杂度:$\Theta(2^n\times n^2)$。
期望得分:$12$分。
实际得分:$12$分。
总得分:$12$分。
测试点$4$:$n=256$
考虑$DP$。
预处理$f[i][j]$表示从$i$到$j$可以分到一组。
定义$dp[i][j]$表示,此时已经分到了第$i$个位置,已经分了$k$组,方案可行$or$不可行($0$/$1$表示。
则$DP$式子为:$dp[i][k]|=dp[j][k-1]\&\&f[j+1][i]$。
在开一个数组记录一下上次转移的位置,为保证最优性,已经转移过不再更新,判断一下就好了。
时间复杂度:$\Theta(n^3)$。
期望得分:$16$分。
实际得分:$24$分。
总得分:$24$分。
测试点$5,6$:$n=1024$
发现上面的解法中$k$那一维可以去掉,即:$if(f[j+1][i])dp[i]=\min(dp[i],dp[j]+1)$。
时间复杂度:$\Theta(n^2)$。
期望得分:$24$分。
实际得分:$24$分。
测试点$7\thicksim 10$:$n=131072$
根据我们刚刚的结论,细心的你一定会发现:
其实这个$DP$并不是一个$DP$,本质上是找最长的合法分段,然后把问题转化为子问题。
问题可以转化为:从前往后,选择一段最长的合法区间并分割,重新进行知道完成为止。
为什么要从后往前呢?因为我们要保证在最优的情况下断点的字典序最小,所以我们需要让断点尽可能的靠前,然而从前往后搜我们并不知道什么时候该断才是最优的,所以我们就可以从后往前搜,尽可能的靠后断,就能保证答案最优了。
我们可以在每插入一个点时逐一判断是否不合法。
时间复杂度:$\Theta(n^2)$。
期望得分:$24$分。
实际得分:$32$分。
总得分:$32$分。
显然上面的这种方法会让你$T$到飞起,所以考虑如何优化。
悄悄的观察数据范围会惊喜的发现$a_i\leqslant 131072$,那么就意味着$a_i+a_j=262144={512}^2$,所以我们在插入一个数的时候可以枚举$x$,看$x^2-a_j$有没有出现过即可。
时间复杂度:$\Theta(n\sqrt{n})$。
期望得分:$40$分。
实际得分:$40$分。
总得分:$40$分。
这样,我们对$K=1$的情况就拿满分了。
对于$K==2$:
测试点$11$:$n=2$
无论如何,一组也够了(出题人好良心……)。
直接$puts("1$\ $n");$就好啦。
时间复杂度:$\Theta(1)$。
期望得分:$4$分。
实际得分:$4$分。
总得分:$44$分。
测试点$12\thicksim 14$:
枚举分组,判定是否合法。
如何判定合法?
可以再次进行暴力分组。
时间复杂度:$\Theta(3^n)$。
其实没有必要,把会发生矛盾的兔子连边,发现这就是个二分图判定,黑白染色即可。
时间复杂度:$\Theta(2^n\times n)$。
期望得分:$16$分。
实际得分:$16$分。
总得分:$56$分。
测试点$15\thicksim 18$:
根据之前的结论,发现我们就是找判定最长序列。
每次判定时建一个图进行判定,直到非法为止。
时间复杂度:$\Theta(n^3)$。
期望得分:$24$分。
实际得分:$32$分。
总得分:$72$分。
测试点$19\thicksim 25$:
发现我们上面的用二维数组记录敌对关系是一种限制,所以我们可以考虑使用带权并查集来存储关系。
可以仿照$K=1$的思想进行统计答案。
但是需要注意一个问题:
如果某个数出现了多次,如果这个数的二倍是一个完全平方数,那么就不能丢到同一组;否则不管有多少个也不用担心(注意还不能有第三个数与它们敌对)。
时间复杂度:$\Theta(n\times \sqrt{\max(a_i)})$。
期望得分:$60$分。
实际得分:$60$分。
总得分:$100$分。
到这里,我们就成功的解决了这道题~~~
代码时刻
测试点$1$:
#include<bits/stdc++.h>
using namespace std;
int a[131073];
int main()
{
int n,K;
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(K==1)
{
if(n==2)
if((int)sqrt(a[1]+a[2])*(int)sqrt(a[1]+a[2])==a[1]+a[2])puts("2\n1");
else puts("1\n");
}
return 0;
}
测试点$2$:
#include<bits/stdc++.h>
using namespace std;
int a[131073];
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==x+y?0:1;}
void judge0()
{
if(asksqr(a[1],a[2])&&asksqr(a[1],a[3])&&asksqr(a[1],a[4])&&asksqr(a[2],a[3])&&asksqr(a[2],a[4])&&asksqr(a[3],a[4])){puts("1\n");exit(0);}
}
void judge1()
{
if(asksqr(a[2],a[3])&&asksqr(a[2],a[4])&&asksqr(a[3],a[4])){puts("2\n1");exit(0);}
if(asksqr(a[1],a[2])&&asksqr(a[3],a[4])){puts("2\n2");exit(0);}
if(asksqr(a[1],a[2])&&asksqr(a[1],a[3])&&asksqr(a[2],a[3])){puts("2\n3");exit(0);}
}
void judge2()
{
if(asksqr(a[3],a[4])){puts("3\n1\n2");exit(0);}
if(asksqr(a[2],a[3])){puts("3\n1\n3");exit(0);}
if(asksqr(a[1],a[2])){puts("3\n3\n3");exit(0);}
}
void judge3(){puts("4\n1\n2\n3");exit(0);}
int main()
{
int n,K;
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(K==1)
{
if(n==4)
{
judge0();
judge1();
judge2();
judge3();
}
}
return 0;
}
测试点$3$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[131073];
int ans[131072],cnt;
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==x+y?1:0;}
bool judge()
{
for(int i=1;i<=cnt;i++)
for(int j=ans[i-1]+1;j<ans[i];j++)
for(int k=j+1;k<=ans[i];k++)
if(asksqr(a[j],a[k]))return 0;
for(int i=ans[cnt]+1;i<n;i++)
for(int j=i+1;j<=n;j++)
if(asksqr(a[i],a[j]))return 0;
return 1;
}
void print()
{
printf("%d\n",cnt+1);
for(int i=1;i<=cnt;i++)
printf("%d ",ans[i]);
}
void dfs(int x,int l,int w)
{
if(x==w){if(judge()){print();exit(0);}return;}
for(int i=l;i<n;i++)
{
ans[++cnt]=i;
dfs(x+1,i+1,w);
cnt--;
}
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(K==1)
{
for(int i=0;i<n;i++)dfs(0,1,i);
}
return 0;
}
测试点$4$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[131073];
bool f[1050][1050],dp[1050][1050];
int flag[1050][1050];
int ans[1050];
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==(x+y)?0:1;}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
dp[i][i]=1;
}
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
for(int k=i;k<j;k++)
if(!asksqr(a[j],a[k]))goto nxt;
f[i][j]=f[j][i]=1;
}
nxt:;
}
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
for(int k=1;k<=n;k++)
{
dp[i][k]|=dp[j][k-1]&f[j+1][i];
if(dp[j][k-1]&&f[j+1][i]&&!flag[i][k])flag[i][k]=j;
}
for(int i=1;i<=n;i++)
if(dp[n][i])
{
printf("%d\n",i);
int now=n;
int k=i;
while(now)
{
ans[++ans[0]]=flag[now][k];
now=flag[now][k--];
}
break;
}
for(int i=ans[0]-1;i;i--)
printf("%d ",ans[i]);
return 0;
}
测试点$5,6$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[131073];
bool f[1050][1050];
int dp[1050];
int flag[1050][1050];
int ans[1050];
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==(x+y)?0:1;}
int main()
{
scanf("%d%d",&n,&K);
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
for(int k=i;k<j;k++)
if(!asksqr(a[j],a[k]))goto nxt;
f[i][j]=f[j][i]=1;
}
nxt:;
}
for(int i=1;i<=n;i++)
for(int j=0;j<i;j++)
if(f[j+1][i])
{
dp[i]=min(dp[i],dp[j]+1);
if(!flag[i][dp[j]+1])flag[i][dp[j]+1]=j;
}
printf("%d\n",dp[n]-1);
int now=n;
int k=dp[n];
while(now)
{
ans[++ans[0]]=flag[now][k];
now=flag[now][k--];
}
for(int i=ans[0]-1;i;i--)
printf("%d ",ans[i]);
return 0;
}
测试点$7\thicksim 10$($32$分):
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[150000];
bool flag[300000];
int que[150000],sum;
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=512;i++)flag[i*i]=1;
if(K==1)
{
int l=n;
for(int i=n;i;i--)
for(int j=l;j>i;j--)
if(flag[a[i]+a[j]])
{
l=i;
que[++sum]=i;
break;
}
printf("%d\n",sum+1);
for(int i=sum;i;i--)printf("%d ",que[i]);
}
return 0;
}
测试点$7\thicksim 10$($40$分):
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[150000],maxn;
bool vis[150000];
int que[150000],sum;
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++){scanf("%d",&a[i]);maxn=max(maxn,a[i]);}
if(K==1)
{
for(int i=n,j=n;i;)
{
for(;j;j--)
{
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<=0)continue;
if(vis[k*k-a[j]])goto nxt;
}
vis[a[j]]=1;
}
nxt:
if(!j)break;
que[++sum]=j;
for(;i>j;i--)vis[a[i]]=0;
}
printf("%d\n",sum+1);
for(int i=sum;i;i--)printf("%d ",que[i]);
}
return 0;
}
测试点$11$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[150000];
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(K==2)if(n==2)puts("1\n");
return 0;
}
测试点$12\thicksim 15$(二分图):
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[131073];
int ans[131072],col[131072],que[131072],cnt;
bool Map[30][30],flag[30],vis[30];
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==x+y?1:0;}
bool color_solve(int x,int color)//判定二分图
{
col[x]=color;
vis[x]=1;
for(int i=1;i<=n;i++)
{
if(i==x||!flag[i]||!Map[x][i])continue;
if(col[i]==color)return 1;
if(!col[i]&&color_solve(i,-color))return 1;
}
return 0;
}
bool judge()
{
for(int i=1;i<=cnt;i++)
{
que[0]=0;
memset(vis,0,sizeof(vis));
memset(flag,0,sizeof(flag));
memset(col,0,sizeof(col));
for(int j=ans[i-1]+1;j<=ans[i];j++)
{que[++que[0]]=j;flag[j]=1;}
for(int j=1;j<=que[0];j++)
if(!vis[que[j]])if(color_solve(que[j],1))return 0;
}
que[0]=0;
memset(vis,0,sizeof(vis));
memset(flag,0,sizeof(flag));
memset(col,0,sizeof(col));
for(int i=ans[cnt]+1;i<=n;i++)
{que[++que[0]]=i;flag[i]=1;}
for(int i=1;i<=que[0];i++)
if(!vis[que[i]])if(color_solve(que[i],1))return 0;
return 1;
}
void print()
{
printf("%d\n",cnt+1);
for(int i=1;i<=cnt;i++)
printf("%d ",ans[i]);
}
void dfs(int x,int l,int w)
{
if(x==w){if(judge()){print();exit(0);}return;}
for(int i=l;i<n;i++)
{
ans[++cnt]=i;
dfs(x+1,i+1,w);
cnt--;
}
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
Map[i][j]=Map[j][i]=asksqr(a[i],a[j]);
if(K==2)
{
for(int i=0;i<n;i++)
dfs(0,1,i);
}
return 0;
}
测试点$15\thicksim 18$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[131073];
int ans[131072],col[131072],que[131072],sum;
bool Map[1050][1050],flag[1050],vis[1050];
bool asksqr(int x,int y){return (int)sqrt(x+y)*(int)sqrt(x+y)==x+y?1:0;}
bool color_solve(int x,int color)
{
col[x]=color;
vis[x]=1;
for(int i=1;i<=n;i++)
{
if(i==x||!flag[i]||!Map[x][i])continue;
if(col[i]==color)return 1;
if(!col[i]&&color_solve(i,-color))return 1;
}
return 0;
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<n;i++)
for(int j=i+1;j<=n;j++)
Map[i][j]=Map[j][i]=asksqr(a[i],a[j]);
if(K==2)
{
int l=n;
for(int i=n;i;i--)
{
que[0]=0;
memset(vis,0,sizeof(vis));
memset(flag,0,sizeof(flag));
memset(col,0,sizeof(col));
for(int j=l;j>=i;j--)
{
que[++que[0]]=j;
flag[j]=1;
}
for(int j=1;j<=que[0];j++)
if(!vis[que[j]])
if(color_solve(que[j],1))
{
l=i;
ans[++sum]=i;
break;
}
}
printf("%d\n",sum+1);
for(int i=sum;i;i--)printf("%d ",ans[i]);
}
return 0;
}
测试点$19\thicksim 25$:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[150000],maxn;
bool vis[150000],sit[150000];
int que[150000],sum;
int fa[300000];
int find(int x){return fa[x]<=0?x:fa[x]=find(fa[x]);}
void connect(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
{
if(fa[x]>fa[y]){fa[y]+=fa[x];fa[x]=y;}
else {fa[x]+=fa[y];fa[y]=x;}
}
}
bool judge(int x, int y)
{
int flag1=find(x),flag2=find(x+140000),flag3=find(y),flag4=find(y+140000);
if(flag1==flag3)return 1;
if(flag2==flag4)return 1;
connect(flag1,flag4);
connect(flag2,flag3);
return 0;
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++){scanf("%d",&a[i]);maxn=max(maxn,a[i]);}
if(K==2)
{
for(int i=n,j=n;i;)
{
for(;j;j--)
{
if(vis[a[j]])
{
if((int)sqrt(a[j]<<1)*(int)sqrt(a[j]<<1)==a[j]<<1)
{
if(sit[a[j]])break;
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<0)continue;
if(vis[k*k-a[j]]&&k*k!=a[j]<<1)goto nxt;
}
sit[a[j]]=1;
}
}
else
{
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<=0)continue;
if(vis[k*k-a[j]]&&(judge(k*k-a[j],a[j])||sit[a[j]]||sit[k*k-a[j]])){fa[a[j]]=fa[a[j]+140000]=0;goto nxt;}
}
vis[a[j]]=1;
}
}
nxt:
if(!j)break;
que[++sum]=j;
for(;i>j;i--)fa[a[i]]=fa[a[i]+140000]=vis[a[i]]=sit[a[i]]=0;
}
printf("%d\n",sum+1);
for(int i=sum;i;i--)printf("%d ",que[i]);
}
return 0;
}
$100$分代码:
#include<bits/stdc++.h>
using namespace std;
int n,K;
int a[150000],maxn;
bool vis[150000],sit[150000];
int que[150000],sum;
int fa[300000];
void solve1()
{
for(int i=n,j=n;i;)
{
for(;j;j--)
{
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<=0)continue;
if(vis[k*k-a[j]])goto nxt;
}
vis[a[j]]=1;
}
nxt:
if(!j)break;
que[++sum]=j;
for(;i>j;i--)vis[a[i]]=0;
}
}
int find(int x){return fa[x]<=0?x:fa[x]=find(fa[x]);}
void connect(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
{
if(fa[x]>fa[y]){fa[y]+=fa[x];fa[x]=y;}
else {fa[x]+=fa[y];fa[y]=x;}
}
}
bool judge(int x, int y)
{
int flag1=find(x),flag2=find(x+140000),flag3=find(y),flag4=find(y+140000);
if(flag1==flag3)return 1;
if(flag2==flag4)return 1;
connect(flag1,flag4);
connect(flag2,flag3);
return 0;
}
void solve2()
{
for(int i=n,j=n;i;)
{
for(;j;j--)
{
if(vis[a[j]])
{
if((int)sqrt(a[j]<<1)*(int)sqrt(a[j]<<1)==a[j]<<1)
{
if(sit[a[j]])break;
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<0)continue;
if(vis[k*k-a[j]]&&k*k!=a[j]<<1)goto nxt;
}
sit[a[j]]=1;
}
}
else
{
for(int k=1;k*k-a[j]<=maxn;k++)
{
if(k*k-a[j]<=0)continue;
if(vis[k*k-a[j]]&&(judge(k*k-a[j],a[j])||sit[a[j]]||sit[k*k-a[j]])){fa[a[j]]=fa[a[j]+140000]=0;goto nxt;}
}
vis[a[j]]=1;
}
}
nxt:
if(!j)break;
que[++sum]=j;
for(;i>j;i--)fa[a[i]]=fa[a[i]+140000]=vis[a[i]]=sit[a[i]]=0;
}
}
int main()
{
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++){scanf("%d",&a[i]);maxn=max(maxn,a[i]);}
if(K==1)solve1();
else solve2();
printf("%d\n",sum+1);
for(int i=sum;i;i--)printf("%d ",que[i]);
return 0;
}
rp++
[洛谷P3940]:分组(贪心+并查集)的更多相关文章
- bzoj3673 & bzoj3674 & 洛谷P3402 可持久化并查集
题目:bzoj3673:https://www.lydsy.com/JudgeOnline/problem.php?id=3673 bzoj3674:https://www.lydsy.com/Jud ...
- 洛谷P1525关押罪犯——并查集
题目:https://www.luogu.org/problemnew/show/P1525 并查集+贪心,从大到小排序,将二人分在不同房间,找到第一个不满足的即为答案. 代码如下: #include ...
- 洛谷 3295 [SCOI2016]萌萌哒——并查集优化连边
题目:https://www.luogu.org/problemnew/show/P3295 当要连的边形如 “一段区间内都是 i 向 i+L 连边” 的时候,用并查集优化连边. 在连边的时候,如果要 ...
- 洛谷P2024 食物链 [NOI2001] 并查集
正解:并查集 解题报告: 传送门(咕了! 其实没有很难(虽然我是交了三发才过的QAQ 但是一来好久没打并查集了恢复一下智力 二来看着智推里唯一一个蓝就很不爽(,,,虽然做了这题之后又补上了个蓝题QAQ ...
- 洛谷P1197 [JSOI2008] 星球大战 [并查集]
题目传送门 星球大战 题目描述 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系. 某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这 ...
- 洛谷 P1551 亲戚(并查集模板)
嗯... 题目链接:https://www.luogu.org/problemnew/show/P1551 思路: 很显然地我们会发现,这是一道并查集的模板题,并且是考察了并查集中的”并“和”查“的操 ...
- 洛谷P1111修复公路并查集改
看了他们的题解感觉很震惊,为什么要用kruskal,这题要用到最小生成树吗??? 38行短短的程序就可以了,我觉得学习不是一种套用,套自己学的,而且题解很大一部分都是kruskal. 个人认为自己的程 ...
- 洛谷 - P5429 - Fence Planning - 并查集
https://www.luogu.org/problemnew/show/P5429 很明显是要维护整个连通块的共同性质,并查集一搞就完事了. #include<bits/stdc++.h&g ...
- 洛谷 - P4997 - 不围棋 - 并查集 - 模拟
https://www.luogu.org/problemnew/show/P4997 首先是改变气的定义,使得容易计算,这个很好理解. 然后使用并查集,因为要维护整个连通块的性质. 最后的难点在于, ...
随机推荐
- Rocketmq-概念
一.Rcoketmq Rocketmq是一个消息中间件,简单来说就是传递消息用的. 二.Rocketmq构成 1.Rocketmq组件不是单个的软件,它是由四个组件构成的: (1)Producer 消 ...
- 《剑指offer》面试题22 栈的压入、弹出序列 Java版
(输入两个整数序列,第一个序列是一串数字的压入顺序,判断第二个序列是否是该栈数字的弹出顺序.) 我的方法:压入序列是给定的,每一次弹出操作形成一个弹出序列的值,我们从前往后遍历弹出序列,每一次访问弹出 ...
- c++ Oracle OCCI 编程
转载备忘:http://blog.sina.com.cn/s/blog_53a72add01015zj4.html 关于occi编程可以参考的链接: http://blog.itpub.net/162 ...
- luogu P1398 [NOI2013]书法家
传送门 注意到\(N\ O\ I\)三个字母都可以从左到右拆成三部分,即\(N=\)一个矩形+一堆矩形+一个矩形,\(O=\)一条+两条横的+一条,\(I=\)两条横的+一个矩形+两条横的,所以可以拆 ...
- numpy.random.randn()和numpy.random.rand()
1 numpy.random.rand() (1)numpy.random.rand(d0,d1,…,dn) rand函数根据给定维度生成[0,1)之间的数据,包含0,不包含1 dn表格每个维度 返回 ...
- C#基础知识之正则表达式
正则表达式 是一种匹配输入文本的模式..Net 框架提供了允许这种匹配的正则表达式引擎.模式由一个或多个字符.运算符和结构组成. 实例 下面的实例匹配了以 'S' 开头的单词: using Syste ...
- Ubuntu 下串口调试工具
1. cutecom 安装:sudo apt-get install cutecom 打开方式: 在终端输入:cutecom,即可打开串口工具 或者在应用中,点击 cutecom 图标打开 打开后的界 ...
- MySQL--limit使用注意
limit m,n 的意义是在选择.查询得到的结果中,从第m条开始,拿连续的n条作为结果返回.根据它的原理可以知道,select ....limit m,n时要扫描得到的数据条数是m+n条.这就导致m ...
- selenium定位
https://www.cnblogs.com/programer-xinmu78/p/10881766.html https://www.cnblogs.com/eastonliu/p/908830 ...
- Git回滚到指定的commit
查看历史commint $ git log (可以记下sha码) 回退命令: $ git reset --hard HEAD^ 回退到上个版本$ git reset --hard HEAD~3 回退到 ...