【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);
}

浙公网安备 33010602011771号