1 条题解
-
0
自动搬运
来自洛谷,原作者为

Demeanor_Roy
小时候我们总想去改变别人,后来发现,比起改变,筛选是性价比更高的事。搬运于
2025-08-24 22:27:39,当前版本为作者最后更新于2022-10-16 19:34:22,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
-
来一个不一样的做法。
-
考虑 暴力。首先将圣诞树按高度从小到大排序,不难想到确定高度最小的一棵圣诞树,枚举其它 棵圣诞树与它构成等差数列前两项,再贪心地能选择选, 判断剩下的圣诞树是否可行。
-
乍一看这个做法是 的,可实际上每往序列贪心地加入一项后,我们都需要判断一次,而不是全部加完才判断。这是因为第一个序列不是选得越多越好,可能它中的某一个元素需要作为第二个序列的桥梁,所以需要多次判断。
-
思考如何优化?仔细观察就会发现,确定一个序列的前两项根本不需要枚举 次,根据鸽巢原理可知,用最小的三棵圣诞树就能确定一个序列的前两项。所以其实只需要判断 作为序列前两项就行了。时间复杂度降至 。
-
最后考虑用数据结构优化判断剩下的数能否构成等差数列的过程。我们发现每次往一个序列中贪心地加入一项,就是在另一个序列中删去一项。用链表维护被删的那个序列,同时用 STL 中的 map 维护相邻元素的差及个数,每次删去一个数就动态维护一下 map,判断能否构成等差序列就是判断第一项和第二项的差的个数是否为元素个数减一。于是时间复杂度优化到了 。
-
细节:需要特判一下 的情况。
-
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e5+10; int n,val[N],L[N],R[N]; bool st[N]; map<int,int> mp; inline bool check(int a,int b) { mp.clear(); memset(L,0,sizeof L); memset(R,0,sizeof R); memset(st,0,sizeof st); st[a]=st[b]=true; int last=0,cnt=2; for(int i=1;i<=n;i++) { if(st[i]) continue; if(last) mp[val[i]-val[last]]++; L[i]=last,R[last]=i; last=i; } if((int)mp[val[R[R[0]]]-val[R[0]]]==n-cnt-1) return true; last=b; for(int i=1;i<=n;i++) { if(st[i]||val[i]-val[last]!=val[b]-val[a]) continue; if(L[i]) mp[val[i]-val[L[i]]]--; if(R[i]) mp[val[R[i]]-val[i]]--; if(L[i]&&R[i]) mp[val[R[i]]-val[L[i]]]++; R[L[i]]=R[i];L[R[i]]=L[i]; cnt++;last=i;st[i]=true; if((int)mp[val[R[R[0]]]-val[R[0]]]==n-cnt-1) return true; } return false; } inline void print() { int num=0; for(int i=1;i<=n;i++) if(st[i]) num++; printf("%d\n",num); for(int i=1;i<=n;i++) if(st[i]) printf("%d ",val[i]); printf("\n%d\n",n-num); for(int i=1;i<=n;i++) if(!st[i]) printf("%d ",val[i]); } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&val[i]); sort(val+1,val+n+1); if(n==2) printf("1\n%d\n1\n%d",val[1],val[2]); else if(check(1,2)||check(1,3)||check(2,3)) print(); else printf("-1"); return 0; }- 完结撒花~
- 1
信息
- ID
- 5881
- 时间
- 1000ms
- 内存
- 500MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者