题解 [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;
}