P3940 分组

https://www.luogu.org/problemnew/show/P3940

官方题解http://pan.baidu.com/s/1eSAMuXk

分析:

  并查集。

  首先根据K=1和K=2分成两个问题来做。

  K=1:问题为分成最小数量的区间,使得每个区间满足:任意两个数的和都不是完全平方数(1,3位于一个集合是不可以的),输出字典序最小的方案。

  一个性质:一个集合越长越好(ppt里有证明)。那么从后往前扫,每到一个判断是否可以加进去,不可以的时候,记录答案。如果直接判断是$n^2$,可以枚举$x^2$来判断,复杂度$O(n\sqrt n)$。

  K=2:问题为分成最小数量的区间,使得每个区间满足:区间可以任意划分为至多两个集合,这两个集合内部任意两个数的和都不是完全平方数。(1,3,5是可以在分成一段的,因为可以划分为{1,3},{5})

  上面的性质还是适用的,所以同样是从后往前扫,每扫到一个判断是否可以加入。这个地方可以记录一个并查集,维护“敌人”集合。$x+n$表示x的“敌人”(不可以一起存在的)。那么如果两个数a+b属于同一个集合,说明a和b用一个共同的敌人,那么a和b就必须在同一个集合里。加入一个x,判断是否可以$k^2-x$共存在这个区间里。注意到一个问题:如果一个数出现了多次,如果它是$2x=k^2$的形式,需要特判(见代码),其他的都可以放进一个集合里。

代码:

 #include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL; inline int read() {
int x=,f=;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-;
for(;isdigit(ch);ch=getchar())x=x*+ch-'';return x*f;
} const int N = ; int a[N], fa[N << ];
int n, Mx;
bool vis[N], Ban[N], issqr[N << ];
vector<int> ans; void solve1() {
for (int j = n, i = n; i>=; ) {
for (bool flag = ; j >= ; -- j) {
for (int k=, tmp; ; ++ k) {
tmp = k * k - a[j];
if (tmp <= ) continue; if (tmp > Mx) break;
if (vis[tmp]) { flag = ; break; }
}
if (!flag) break;
vis[a[j]] = true;
}
if (!j) break;
ans.push_back(j);
for (; i > j; -- i) vis[a[i]] = false;
}
} int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
} bool check(int u,int v) {
int u1 = find(u), u2 = find(u + Mx);
int v1 = find(v), v2 = find(v + Mx);
if (u1 == v1) return ;
fa[u1] = v2; fa[v1] = u2;
return ;
} void solve2() {
for (int i = ; i <= (Mx << ); ++i) fa[i] = i;
for (int i = ; i * i <= (Mx << ); ++i) issqr[i * i] = ;
for (int i = n, j = n; i; ) {
for (bool flag = ; j; --j) {
if (!vis[a[j]]) { // 未出现过
for (int k = , tmp; ; ++k) {
tmp = k * k - a[j];
if (tmp <= ) continue; if (tmp > Mx) break;
if (vis[tmp] && (Ban[a[j]] || Ban[k * k - a[j]] || check(k * k - a[j], a[j]))) {
flag = ; fa[a[j]] = a[j], fa[a[j] + Mx] = a[j] + Mx; break;
}
}
if (!flag) break;
vis[a[j]] = true;
}
// 出现过:只有2*a=x^2(2,8...)的情况要特判,其他的都可以分到一集合里。
// 这些数最多出现两个,而且没有第三个敌人
else if (issqr[a[j] + a[j]]) {
if (Ban[a[j]]) break;
for (int k = , tmp; ; ++k) { // 判断是否有第三个敌人
tmp = k * k - a[j];
if (tmp < ) continue; if (tmp > Mx) break;
if (vis[tmp] && k * k != a[j] * ) { flag = ; break; }
}
if (!flag) break;
Ban[a[j]] = true; // a[j]以前一个,现在一个 a[j]以后不能再有了。
}
}
if (!j) break;
ans.push_back(j);
for (; i > j; --i) fa[a[i]] = a[i], fa[a[i] + Mx] = a[i] + Mx, vis[a[i]] = Ban[a[i]] = ;
}
} int main() {
n = read();int K = read();
for (int i=; i<=n; ++i) a[i] = read(), Mx = max(Mx, a[i]);
if (K == ) solve1();
else solve2();
printf("%d\n",ans.size() + );
for (int i=ans.size()-; i>=; --i) printf("%d ",ans[i]); putchar('\n');
return ;
}

P3940 分组的更多相关文章

  1. 2019.8.3 NOIP模拟测试12 反思总结【P3938 斐波那契,P3939 数颜色,P3940 分组】

    [题解在下面] 早上5:50,Gekoo同学来到机房并表态:“打暴力,打暴力就对了,打出来我就赢了.” 我:深以为然. (这是个伏笔) 据说hzoi的人还差两次考试[现在是一次了]就要重新分配机房,不 ...

  2. [洛谷P3940]:分组(贪心+并查集)

    题目传送门 题目描述 小$C$在了解了她所需要的信息之后,让兔子们调整到了恰当的位置.小$C$准备给兔子们分成若干个小组来喂恰当的胡萝卜给兔子们吃.此时,$n$只兔子按一定顺序排成一排,第$i$只兔子 ...

  3. 题解 P3940 分组

    有些梦想虽然遥不可及,但不是不可能实现.只要我足够的强. 前言 调了挺长时间的,并查集合并的时候需要 find 一下,不然会炸内存.... 解题思路 参考了题解区一篇思路非常好的题解,在这里讲一下自己 ...

  4. 一条Sql语句分组排序并且限制显示的数据条数

    如果我想得到这样一个结果集:分组排序,并且每组限定记录集的数量,用一条SQL语句能办到吗? 比如说,我想找出学生期末考试中,每科的前3名,并按成绩排序,只用一条SQL语句,该怎么写? 表[TScore ...

  5. xamarin android ListView手动分组

    xamarin的listview控件其实自带有分组方法,关于xamarin listview的自带分组方法请自行参考官方文档,我这里只写自己写的分组方法.xamarin自带的分组好是好,功能多,但是加 ...

  6. [Java Collection]List分组之简单应用.

    前言 今天有一个新需求, 是对一个List进行分组, 于是便百度到一些可用的代码以及我们项目使用的一些tools, 在这里总结下方便以后查阅. 一: 需求 现在我们一个数据库表t_series_val ...

  7. TSQL 分组集(Grouping Sets)

    分组集(Grouping Sets)是多个分组的并集,用于在一个查询中,按照不同的分组列对集合进行聚合运算,等价于对单个分组使用“union all”,计算多个结果集的并集.使用分组集的聚合查询,返回 ...

  8. SQL Server 动态行转列(参数化表名、分组列、行转列字段、字段值)

    一.本文所涉及的内容(Contents) 本文所涉及的内容(Contents) 背景(Contexts) 实现代码(SQL Codes) 方法一:使用拼接SQL,静态列字段: 方法二:使用拼接SQL, ...

  9. SolrNet高级用法(分页、Facet查询、任意分组)

    前言 如果你在系统中用到了Solr的话,那么肯定会碰到从Solr中反推数据的需求,基于数据库数据生产索引后,那么Solr索引的数据相对准确,在电商需求中经常会碰到菜单.导航分类(比如电脑.PC的话会有 ...

随机推荐

  1. mongo数据库导入导出数据

    一.Mongodb导出工具mongoexport Mongodb中的mongoexport工具可以把一个collection导出成JSON格式或CSV格式的文件.可以通过参数指定导出的数据项,也可以根 ...

  2. BZOJ2761:[JLOI2011]不重复数字(map)

    Description 给出N个数,要求把其中重复的去掉,只保留第一次出现的数. 例如,给出的数为1 2 18 3 3 19 2 3 6 5 4,其中2和3有重复,去除后的结果为1 2 18 3 19 ...

  3. Odoo字段类型

    转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/9278620.html    一:基本字段类型 Binary:二进制类型,用于保存图片.视频.文件.附件等,在视 ...

  4. 日常踩坑——Dev C++ pow()函数的坑

    坑 Dev C++ pow()函数 那年冬天,显示屏前坐着如喽啰,那时候我含泪发誓,再也不用Dev. 蓝桥杯官网给提供的版本,没办法bug也得硬着头皮用. 16年蓝桥杯的第八题 四平方和定理: 在De ...

  5. robotframwork的WEB功能测试(一)—切换window窗口

    selenium2library提供的切换到新窗口的关键字,只有select window,而且也只能根据title.name.url去定位.如下图所示,明显在实际使用中是不够的. 所以这里总结了一下 ...

  6. git编译安装报错 http-push.c:20:19: 警告:expat.h:没有那个文件或目录

    解决: [root@hdoop3 git-2.18.1]# yum install expat-devel

  7. 深入理解Java虚拟机(一) 运行时数据区划分

    前言:从我学Java的第一天开始,我的大学老师就告诉我 Java语言相比C.C++的语言有一个非常强大的功能,那就是自动内存管理:我们用Java编码时不需要申请或释放内存等,这些工作全部交由我们的Ja ...

  8. oracle创建用户、表空间、临时表空间、分配权限步骤详解

    首先登陆管理员账号,或者有DBA权限的用户,接下来依次: --查询所有用户select * from dba_users;--创建新用户create user gpmgt identified by ...

  9. iOS笔记,开发经验总结【持续更新】

    1. 设置navigationBar 背景颜色有色差, 原因:如果单纯的设置背景颜色也是有高斯模糊处理的效果,对纯色高斯模糊处理过后相当于纯色的70%(猜测)透明化处理,但是反正就是有色差 解决方法一 ...

  10. MongoDB的入门

    MongoDB mongodb是非关系型数据库 对于关系型数据库,存储数据的时候需要提前建表建库,随着数据的复杂度越来越高,所建的表的数量也越来越多:但是非关系型却不需要 mongodb的基本的命令的 ...