七海ノ心象素描

所有的为时已晚都是恰逢其时

Nanami^2 avatar

Author

Nanami^2

大一学生 | 不是很聪明的样子

Telegram

我的频道

不定期推送灵感笔记

t.me/kisanami_blog
联系方式
QQ 群
1072614878
发送邮件

luogu3940 分组 题解

Solution

Nanami^2
Nanami^2 Even in the rain.
2023年08月10日
预计阅读 4 分钟
868 字

对于 K=1K=1 的情况,每组里面都不能有冲突,所以从后往前尽可能划分,容易证明这样做是对的。

如何判断冲突?注意到值域不大,可以开一个桶,枚举一个完全平方数 k2k^2,判断 k2xk^2-x 是否出现过即可。完全平方数的个数是 O(n)O(\sqrt{n}) 的,所以复杂度为 O(nn)O(n \sqrt{n})

对于 K=2K=2 的情况,如果我们把有冲突的点连边,那么每一组的点都构成一张二分图。

可以像经典题「关押罪犯」中一样,用拆点并查集维护。

v(x)v(x) 表示 xx 是否出现过,v2(x)v_2(x) 表示 xx 是否出现了超过 11 次并且 2x2x 是完全平方数。

如果 xx 没有出现过,v(k2x)=1v(k^2-x)=1,分两种情况。

  1. 如果 xxk2xk^2-x 被合并进了同一个集合,并查集产生冲突,那么 xx 不能加入当前组。
  2. v2(k2x)=1v_2(k^2-x)=1,那么 xx 不能加入当前组。

如果 xx 出现过,分两种情况。

  1. 如果 2x2x 不是完全平方数,由于 xx 与组内其他点能构成二分图,所以加入 xx 依然能。
  2. 如果 2x2x 是完全平方数。令 v2(x)=1v_2(x)=1。枚举 k2k^2,如果 v(k2x)=1v(k^2-x)=1 并且 k22xk^2\neq2x,那么说明会产生冲突,xx 不能加入。注意如果一开始 v2(x)v_2(x) 的值就已经是 11,就直接判掉。
// Problem: P3940 分组
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3940
// Author: yozora0908
// Memory Limit: 250 MB
// Time Limit: 1000 ms
//
// Let's Daze
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define uint unsigned long long
#define PII pair<int,int>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define SET(a,b) memset(a,b,sizeof(a))
#define CPY(a,b) memcpy(a,b,sizeof(b))
#define rep(i,j,k) for(int i=(j);i<=(k);++i)
#define per(i,j,k) for(int i=(j);i>=(k);--i)
int read() {
int a=0, f=1; char c=getchar();
while(!isdigit(c)) {
if(c=='-') f=-1;
c=getchar();
}
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a*f;
}
const int N=131075, lim=131072;
int n, m, K, a[N], ans[N], sqr[2*N];
bool v[N], v2[N];
namespace sub1 {
void solve() {
for(int i=n,j=n;i;) {
for(;j;--j) {
for(int k=1;k*k-a[j]<N;++k) if(a[j]<=k*k) {
if(v[k*k-a[j]]) goto out;
}
v[a[j]]=1;
}
out:;
if(!j) break;
ans[++m]=j;
for(;i>j;--i) v[a[i]]=0;
}
}
};
struct DSU {
int f[2*N];
void init() { for(int i=1;i<=2*lim;++i) f[i]=i; }
int get(int x) { return x==f[x]? x:f[x]=get(f[x]); }
void merge(int x,int y) {
x=get(x), y=get(y);
if(x!=y) f[x]=y;
}
} dsu;
bool check(int x,int y) {
int x1=dsu.get(x), x2=dsu.get(x+lim);
int y1=dsu.get(y), y2=dsu.get(y+lim);
if(x1==y1) return 1;
if(x2==y2) return 1;
dsu.merge(x1,y2);
dsu.merge(x2,y1);
return 0;
}
namespace sub2 {
void solve() {
dsu.init();
for(int i=n,j=n;i;) {
for(;j;--j) {
if(!v[a[j]]) {
for(int k=1;k*k-a[j]<N;++k) if(a[j]<=k*k) {
if(v[k*k-a[j]]) {
if(check(a[j],k*k-a[j])||v2[k*k-a[j]]) goto out;
}
}
v[a[j]]=1;
} else {
if(sqr[2*a[j]]) {
if(v2[a[j]]) goto out;
v2[a[j]]=1;
for(int k=1;k*k-a[j]<N;++k) if(a[j]<=k*k) {
if(v[k*k-a[j]]&&k*k!=2*a[j]) goto out;
}
}
}
}
out:;
if(!j) break;
ans[++m]=j;
for(;i>j;--i) v[a[i]]=v2[a[i]]=0, dsu.f[a[i]]=a[i], dsu.f[a[i]+lim]=a[i]+lim;
}
}
};
signed main() {
n=read(), K=read();
rep(i,1,n) a[i]=read();
for(int i=1;i<=512;++i) sqr[i*i]=1;
if(K==1) sub1::solve();
else sub2::solve();
printf("%d\n",m+1);
per(i,m,1) printf("%d ",ans[i]);
return 0;
}

觉得这篇文章怎么样?

点个赞,让更多人看到!

分享这篇文章

知识因分享而增值

分类

题解

标签

贪心
并查集
二分图

版权声明:本文作者为 Nanami^2,首发于nanami7.top

遵循 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

评论区

本评论区由 EveSunMaple 自主开发