题解 [ICPC2017 WF] Money for Nothing

传送门

啥也不会怎么办?
去 Luogu 上做这题,Luogu 上数据比较离谱,写啥都能过

首先发现有用的供货商按时间排序后价格递减
有用的消费商按时间排序后价格也递减
然后一对 \((i, j)\) 的贡献是 \((q_i-p_j)*(e_i-d_j)\),拆开式子里面有两个变量没办法斜率优化
于是就不会做了

然后正解:
上面那个式子确实不太好优化
但是放到平面直角坐标系上考虑那玩意是个面积
图是从 @灵华 那里盗来的
image
现在有两类点,两个不同类点能确定一个矩形,求最大的那个矩形的面积
貌似是个经典问题来着
直接确定不太好确定,但是这东西看起来比较有单调性
我一开始以为若左下角的点确定了面积关于右上的点单峰来着
但并不是的
于是这个东西的决策单调性体现在与每个一类点匹配的二类点不会交叉
image
证明的话按照上面的图对面积进行一些加减即可
于是可以分治处理
对其中一类分治
具体的,在处理区间 \([l, r]\) 时,找到 \(mid\) 的决策点
那么 \([l, mid-1]\) 的决策点都在 \(mid\) 以左,\([mid+1, r]\) 的决策点都在 \(mid\) 以右
这样只有 \(log\) 层,每层共访问 \(O(n)\) 个元素
复杂度 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define fir first
#define sec second
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
ll ans;
int top1, top2;
pair<ll, ll> a[N], b[N], sta1[N], sta2[N];
inline ll calc(int i, int j) {return max(sta2[j].fir-sta1[i].fir, 0ll)*max(sta2[j].sec-sta1[i].sec, 0ll);}

void solve(int l1, int r1, int l2, int r2) {
	// cout<<"solve: "<<l1<<' '<<r1<<' '<<l2<<' '<<r2<<endl;
	int mid=(l1+r1)>>1, pos; ll val=-1;
	for (int i=l2; i<=r2; ++i) if (sta2[i].fir<=sta1[mid].fir || calc(mid, i)>val)
		ans=max(ans, val=calc(mid, i)), pos=i;
	if (l1<mid) solve(l1, mid-1, l2, pos);
	if (r1>mid) solve(mid+1, r1, pos, r2);
}

signed main()
{
	m=read(); n=read();
	for (int i=1; i<=m; ++i) a[i].fir=read(), a[i].sec=read();
	for (int i=1; i<=n; ++i) b[i].fir=read(), b[i].sec=read();
	sort(a+1, a+m+1, [](pair<ll, ll> a, pair<ll, ll> b){return a.fir==b.fir?a.sec>b.sec:a.fir<b.fir;});
	sort(b+1, b+n+1, [](pair<ll, ll> a, pair<ll, ll> b){return a.fir==b.fir?a.sec>b.sec:a.fir>b.fir;});
	for (int i=1; i<=m; ++i) if (!top1 || a[i].sec<sta1[top1].sec) sta1[++top1]=a[i];
	for (int i=1; i<=n; ++i) if (!top2 || b[i].sec>sta2[top2].sec) sta2[++top2]=b[i];
	reverse(sta2+1, sta2+top2+1);
	// cout<<"sta1: "; for (int i=1; i<=top1; ++i) cout<<"("<<sta1[i].fir<<','<<sta1[i].sec<<") "; cout<<endl;
	// cout<<"sta2: "; for (int i=1; i<=top2; ++i) cout<<"("<<sta2[i].fir<<','<<sta2[i].sec<<") "; cout<<endl;
	solve(1, top1, 1, top2);
	printf("%lld\n", ans);
	
	return 0;
}
posted @ 2022-06-11 15:34  Administrator-09  阅读(4)  评论(0编辑  收藏  举报