分组——题解
分组
题目背景
大样例可在页面底部「附件」中下载。
题目描述
小 C 在了解了她所需要的信息之后,让兔子们调整到了恰当的位置。小 C 准备给兔子 们分成若干个小组来喂恰当的胡萝卜给兔子们吃。
此时,
在分组前,小 C 发现了一个规律:有些兔子会两两发生矛盾。并且,两只兔子会发生矛 盾,当且仅当代表他们的颜色的数值之和为一个正整数的平方。比如,1 色兔子和 2 色兔子 不会发生矛盾,因为 3 不是任何一个正整数的平方;而 1 色兔子却会和 3 色兔子发生矛盾, 因为
小 C 认为,只要一个小组内的矛盾不要过大就行。因此,小 C 定义了一个小组的矛盾 值
小 C 要求,矛盾值最大的小组的矛盾值
字典序最小的方案是指,按顺序排列分组的间隔位置,即所有存在兔子
输入格式
从标准输入中读入数据。
输入第 1 行两个正整数
输入第 2 行
输出格式
输出到标准输出中。
输出第 1 行一个正整数
输出第 2 行
样例 #1
样例输入 #1
5 2
1 3 15 10 6
样例输出 #1
2
1
提示
【样例 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}}。
【数据范围】
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。
每个测试点的数据规模及特点如下表:
特殊性质 1:保证最优分组方案唯一。
特殊性质 2:保证不会有两只相同颜色的兔子。
题解
考虑观察数据范围:
发现
K=1
当
显然,如果区间
要字典序最小的话,可以从后往前取,问题就变成了每一次求出最长段。
因为这个平方数最大为512,大不了可以枚举这个平方数,判断有无数可以与其配对。由于值域与
具体的,每一可以在外层倒序循环
如果扫描完了还没有发现一个为1,那么将
K=2
此时大体思路一致,只是如何判断的问题。此时问题就变成了判断每一段是否可以被分为两个部分.
这个有点像关押罪犯那道题,可以用二分图(将平方数连边)或者扩展域/边带权并查集解决。
只是二分图的话,每次都要跑一边,可能会导致复杂度假掉,考虑并查集。因为扩展域好写,就写扩展域吧.设fa[1~n]
为无矛盾域,f[n+1~n<<1]
为矛盾域
那么具体在内层循环判断的时候,当然若
当合并出现矛盾的时候,就需要重新重置f,S
。当然别忘了都得给
这里有个细节,就是一个段内可能有重复出现的数字,而他们的域不一定相同.此时最好是将vector
需要注意的是,最好是维护一下当前段的起始位置,以便保证重置复杂度
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define N 350000
int f[N << 1], n, m, s1[N], a[N], g[N], cnt;
vector<int>s2[N];
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
f[x] = y;
}
void init() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)scanf("%d", &a[i]);
if (!(m & 1))for (int i = 1; i <= n << 1; i++)f[i] = i;
}
int solve1() {
int ans = 1, id = n;
s1[a[n]] = 1;
for (int i = n - 1; i; --i) {
for (int j = 1; j <= 520; j++) {
if (j * j - a[i] < 0)continue;
if (s1[j * j - a[i]]) {
ans++;
for (int k = i; k <= id; k++)s1[a[k]] = 0;
id = i;
g[++cnt] = id;
break;
}
}
s1[a[i]] = 1;
}
return ans;
}
int solve2() {
int ans = 1, id = n;
s2[a[n]].push_back(n);
for (int i = n - 1; i; --i) {
for (int j = 1; j <= 520; j++) {
if (j * j - a[i] < 0 || !s2[j * j - a[i]].size())continue;
int len = s2[j * j - a[i]].size();
for (int k = 0; k < len; k++) {
merge(i + n, s2[j * j - a[i]][k]);
merge(i, s2[j * j - a[i]][k] + n);
}
}
if (find(i) == find(i + n)) {
ans++;
g[++cnt] = i;
for (int k = i; k <= id; k++) {
if (s2[a[k]].size())s2[a[k]].clear();
f[k] = k, f[k + n] = k + n;
}
id = i;
}
s2[a[i]].push_back(i);
}
return ans;
}
int main() {
init();
if (m & 1)printf("%d\n", solve1());
else printf("%d\n", solve2());
for (int i = cnt; i; --i) {
printf("%d ", g[i]);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!