题解 [PR #4] 赌徒

传送门

因为不会 分式的基本性质 卡在了最后一步,我是什么品种的傻逼

首先发现 \(a, b\) 只可能是 \(a_i, b_i\) 中出现过的数或 1
那么枚举 \((a, b)\) 组合再 \(O(n)\) check 可以做到 \(O(n^3)\)
然后考虑拆一下贡献
发现 \((a, b)\) 和某一个 \((a_i, b_i)\) 产生的贡献形如

\[\frac{1}{4}([a_i\leqslant a]+[b_i\leqslant a]+[a_i\leqslant b]+[b_i\leqslant b]) \]

然后就可以对 \(a, b\) 分别维护前缀和
这样就可以 \(O(n^2)\)

再进一步优化似乎并不容易,因为这个东西完全不可二分之类的
所以考虑枚举 \(b\),优化对于固定的 \(b\) 计算最优 \(a\) 的过程
\(val_i\) 为上文提到的前缀和
则贡献是 \(val_b+\min\{val_a-ab\}\)
考虑点集 \((i, val_i)\),令 \(i<j\)

\[\begin{aligned} val_i-i\times b&<val_j-j\times b\\ val_i-val_j&<(i-j)b\\ \frac{val_i-val_j}{i-j}&>b\\ \frac{val_j-val_i}{j-i}&\color{red}>b \end{aligned}\]

嗯我错在了如图标红的位置
我是什么品种的傻逼

那么容易发现最优的 \(a\)\(y=bx\) 与凸包的切点,二分出来即可
当然发现切点随 \(b\) 增加单调,双指针可以做到 \(O(n)\)

代码鸽了
upd: 模拟赛搬这题,被强迫码了一遍,结果 ans 忘记初始化成 \(-\infty\) 被 hack 了/kk

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 1000010
#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;
int usiz, top;
ll a[N], b[N], x[N], uni[N], sum[N], tot, ans=-INF;
struct line{ll k, b; double p;}sta[N];
inline double qmeet(line a, line b) {return (double)(b.b-a.b)/(a.k-b.k);}

ll query(ll x) {
	auto it=lower_bound(sta+1, sta+top+1, x, [](line a, ll x){return a.p<x;});
	// cout<<"it: "<<it->k<<' '<<it->b<<endl;
	// cout<<"x: "<<x<<endl;
	return it->k*x+it->b;
}

signed main()
{
	n=read();
	uni[++usiz]=1;
	for (int i=1; i<=n; ++i) {
		uni[++usiz]=a[i]=read();
		uni[++usiz]=b[i]=read();
		x[i]=read();
	}
	sort(uni+1, uni+usiz+1);
	usiz=unique(uni+1, uni+usiz+1)-uni-1;
	for (int i=1; i<=n; ++i) {
		sum[lower_bound(uni+1, uni+usiz+1, a[i])-uni]+=2*x[i];
		sum[lower_bound(uni+1, uni+usiz+1, b[i])-uni]+=2*x[i];
		tot+=x[i];
	}
	for (int i=1; i<=usiz; ++i) sum[i]+=sum[i-1];
	for (int i=1; i<=usiz; ++i) {
		line now={-4*uni[i], sum[i]};
		// cout<<"line: "<<-uni[i]<<' '<<sum[i]<<endl;
		while (top>1 && qmeet(now, sta[top])>=sta[top].p) --top;
		now.p=top?qmeet(now, sta[top]):1e30;
		sta[++top]=now;
	}
	reverse(sta+1, sta+top+1);
	for (int i=1; i<=usiz; ++i) {
		// cout<<"x: "<<uni[i]<<endl;
		ans=max(ans, sum[i]+query(uni[i]));
	}
	printf("%lld\n", ans-4*tot);

	return 0;
}
posted @ 2022-07-27 09:29  Administrator-09  阅读(2)  评论(0编辑  收藏  举报