【Luogu比赛-无聊出的小水题by darkf T1摘果实】一道好题
想了想,以后就不在题解标题里写出具体的算法了,想到有可能会有人看到标题后原本准备去做结果兴致全无,以后除非是模板题或特殊情况就最多吐吐槽了。
https://www.luogu.org/contestnew/show/8984
题目自己看,大致题意:给一堆果树,这些果树两个权值,一个个数,一个高度,我们有一些工人,他们都是只有一方面限制(例如可以摘的果树的个数不限制,高度限制多少一下),求最小花费时间,数据范围10万左右
看到第一个想法DP?然后想不出状态更写不出来。之后大概根据数据范围猜到应该是二分+贪心,怎么贪?贪不来又炸了。
考试中没做出来,,,难度 普及+ orz orz orz 果然太弱了额。
考后看标程和题解,才发现开始的大致思路是对的,只是实现上有点问题,堆?平衡树?单调队列?然后就懵圈了。然后在标程里突然注意到了并查集。仔细一看才发现这很强,直接代替了我想用的平衡树。
关于二分时间之后的验证实现方法:我们将A数组 sort,B数组 sort ,将所有果树 按照关键字 果实数量 sort 一下(果实数量多的在前面),然后开始枚举,由于考虑到如果一个对果实有限制的工人可以摘到当前果实了,那么他一定也能将剩下的果实摘到(后面果实数量更少),所以我们优先找对于高度有限制并且可以达到高度比较小的那个。由于这个有序,我们直接upper_bound一下,查询这个的并查集 (注意这个操作),如果这个摘的次数达到时间了,那么我们直接并查集连向比他大的那一个,这样每次我们upper_bound到的都是还没有用完次数并且可以满足条件的!如果在高度限制工人中找完了,就来果实限制中找,我们优先选可达到果实大的!如果这个大的都不满足,那么这些工人不满足了return 0,如果次数全用完了return 0,否则最后 return 1。
就这样一道 普及+题 就做完了。。。我真是太弱了。。。
蒟蒻的参考代码(也可以看答案标程)
#include<iostream> #include<algorithm> #include<cstdio> #include<cmath> #include<queue> using namespace std; const int maxn = 200005; int A,B,N; struct node { int w,h; }z[maxn]; int a[maxn],b[maxn]; bool cmp(node aa,node bb) { if(aa.w!=bb.w) return aa.w>bb.w; return aa.h>bb.h; } int cnta[maxn],cntb[maxn],bcj[maxn]; int gf(int x) { return bcj[x]==x?x:bcj[x]=gf(bcj[x]); } void init() { for(int i=1;i<=A;i++) cnta[i]=0; for(int i=1;i<=B;i++) cntb[i]=0; for(int i=1;i<=B+1;i++) bcj[i]=i; } bool isok(int mid) { init(); int now = A; for(int i=1;i<=N;i++)//w big in the front() { int bbb = upper_bound(b+1,b+1+B,z[i].h)-b; bbb = gf(bbb); if(bbb!=B+1&&cntb[bbb]<mid) { cntb[bbb]++; if(cntb[bbb]==mid) bcj[bbb]=bbb+1; } else { if(!now||z[i].w>=a[now]) return 0; cnta[now]++; if(cnta[now]==mid) now--; } } return 1; } int main() { scanf("%d%d%d",&A,&B,&N); int maxa = 0; int maxb = 0; for(int i=1;i<=A;i++) { scanf("%d",&a[i]); maxa = max(maxa,a[i]); } for(int i=1;i<=B;i++) { scanf("%d",&b[i]); maxb = max(maxb,b[i]); } for(int i=1;i<=N;i++) { scanf("%d%d",&z[i].w,&z[i].h); if(z[i].w>=maxa&&z[i].h>=maxb) { puts("Impossible"); return 0; } } sort(a+1,a+1+A); sort(b+1,b+1+B); sort(z+1,z+1+N,cmp); int L = 1; int R = N; int ans; while(L<=R) { int mid = (L+R)>>1; if(isok(mid)) ans=mid,R=mid-1; else L=mid+1; } printf("%d",ans); }