记录一道很秒的题(不过可能讲不太清楚)
题目描述
给定 n 个边平行于坐标轴的矩形,长宽分别为a i 、b i 。
你可以任意平移矩形,删除至多 m 个矩形,但不允许旋转它们。
问最后所有剩余矩形的交的面积最大是多少。
输入格式
第一行,一个自然数 T,代表数据组数。
对于每组数据:
第一行,一个正整数 n,一个自然数 m。
接下来 n 行,每行两个正整数,a i ,b i 。
输出格式
对于每组数据,输出一行,一个整数,代表答案。
输入数据
3
2 0
5 10
5 5
2 1
1 1
2 2
3 1
3 5
4 4
5 3
输出数据
25
4
12
数据范围
保证0 ≤ m < n,a i , b i ≤ 10 5 。
题目分析
本蒟尽量讲清楚吧,如果讲得不清楚,可以那下面这组样例去试一下。
1
6 4
40630 97454
40920 55282
44485 5555
19097 37430
33481 39964
69237 45535
也许通过这组样例能够发现什么。尽量在下面代码中讲清楚。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int t,n,m;
pair<int,int> a[maxn];
ll ans;
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
ans=0;
for(int i=1;i<=n;++i)
scanf("%d%d",&a[i].first,&a[i].second);
priority_queue<int,vector<int>,greater<int> > q;
sort(a+1,a+n+1);
//这里是双关键字排序(从小到大),pair可以理解成一个容器,或是跟数组差不多的东西吧。具体不清楚
for(int i=m+1;i<=n;++i) q.push(a[i].second);
/*我们先假设答案是在把前m个去掉之后,剩下的数据中产生的
原题中的数据范围是有些提示,比如a[i]是相同的,那我们就可以只比较b[i]就好了
所以这里也就启示我们去想正解可以先把a[i]排序,然后去比较b[i]。
*/
ans=(ll)a[m+1].first*q.top();
//初始的答案
for(int i=m;i;--i){
q.push(a[i].second);
q.pop();
if(ans<(ll)a[i].first*q.top()) ans=(ll)a[i].first*q.top();
/*
上面这里的更新答案可以看成这样:
因为有个原本在堆里的b元素被弹出了(也就相当于原本某块矩形被当前的第i块给替换了)
刚好可以得到一个更优的答案
然后本蒟的朋友提出了某个问题,万一刚刚放进来的f[i].second跟着又被弹出去了
但是后面却用到了f[i].first*q.top(),那不是矛盾了吗?后来本蒟想了想,不会出现这样的情况
为什么呢?你想f[i].first本来就是有序的,且是从小到大的。然后刚刚进来的f[i].second
接着就被弹出,是不是当前这个f[i].second是小于小根堆中任何元素的。
而此时f[i].first又会小于之前的任何f[i].first,所以f[i].first*q.top()会小于
之前所得到的答案,那么也就不会用到这个f[i].first,所以也就不矛盾
*/
}
printf("%lld\n",ans);
}
return 0;
}