传送门
思路
唉,我太弱了,这么之前的题,到现在才会做,唉,我太弱啦!
将逗气聚集点和聚逗阵按位置排序。对于每个聚逗阵,我们把它左边和右边的逗气聚集点分开考虑,且从左向右考虑。下面我们只讨论逗气聚集点在聚逗阵左边的情况,右边的情况与之相似。
设聚逗阵
在聚逗阵
的右边(
),它们都在逗气聚集点
的左边。若
比
优,那么我们有:
整理得:
注意分母的符号一定是正号,因此可以除过来。
根据斜率优化的理论,设不等式左边为 ,如果有 ,那么 永远不会是最优的。所以用一个单调栈维护,寻找决策点在凸包上二分找切点即可。
实际操作上,我习惯写倍增,并且实际找的东西并不是切点(切点只是为了方便理解),而是第一个不满足不等式的点或者栈顶。
参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef LL INT_PUT;
INT_PUT readIn()
{
INT_PUT a = 0; bool positive = true;
char ch = getchar();
while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
if (ch == '-') { positive = false; ch = getchar(); }
while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
return positive ? -a : a;
}
void printOut(INT_PUT x)
{
char buffer[20]; int length = 0;
if (x < 0) putchar('-'); else x = -x;
do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
do putchar(buffer[--length]); while (length);
putchar('\n');
}
const int maxn = int(2e5) + 5;
int n, m;
struct TypeA
{
int a, b;
void read()
{
a = readIn();
b = readIn();
}
bool operator<(const TypeA& y) const
{
return a < y.a;
}
} tas[maxn];
struct TypeB
{
int c, d;
void read()
{
c = readIn();
d = readIn();
}
bool operator<(const TypeB& y) const
{
return c < y.c;
}
} tbs[maxn];
int idx[maxn];
bool comp(const int& x, const int& y)
{
return tbs[x] < tbs[y];
}
int stack[maxn];
int top;
LL f[maxn];
double slope(int j, int k)
{
return (double)(tas[j].b - tas[k].b) / (tas[j].a - tas[k].a);
}
LL DP1(int i, int j)
{
return std::max(LL(0), tas[j].b - (LL)(tbs[i].c - tas[j].a) * tbs[i].d);
}
LL DP2(int i, int j)
{
return std::max(LL(0), tas[j].b - (LL)(tas[j].a - tbs[i].c) * tbs[i].d);
}
void run()
{
n = readIn();
m = readIn();
for (int i = 1; i <= n; i++)
tas[i].read();
for (int i = 1; i <= m; i++)
tbs[i].read();
for (int i = 1; i <= m; i++)
idx[i] = i;
std::sort(tas + 1, tas + 1 + n);
std::sort(idx + 1, idx + 1 + m, comp);
for (int i = 1; i <= m; i++)
f[i] = LLONG_MIN;
int cnt;
cnt = 1;
top = 0;
for (int i = 1; i <= m; i++)
{
const TypeB& tb = tbs[idx[i]];
for (; cnt <= n && tas[cnt].a <= tb.c; cnt++)
{
const TypeA& ta = tas[cnt];
while (top > 1 && slope(cnt, stack[top - 1]) >
slope(stack[top - 1], stack[top - 2]))
top--;
stack[top++] = cnt;
}
if (top)
{
int t = 0;
int k = 0;
while (1 << k < (top - 1)) k++;
for (int j = k; ~j; j--) if (t + (1 << j) < top)
{
if (slope(stack[t + (1 << j)], stack[t + (1 << j) - 1]) > -tb.d)
t += 1 << j;
}
f[idx[i]] = std::max(f[idx[i]], DP1(idx[i], stack[t]));
}
}
cnt = n;
top = 0;
for (int i = m; i; i--)
{
const TypeB& tb = tbs[idx[i]];
for (; cnt && tas[cnt].a >= tb.c; cnt--)
{
const TypeA& ta = tas[cnt];
while (top > 1 && slope(cnt, stack[top - 1]) <
slope(stack[top - 1], stack[top - 2]))
top--;
stack[top++] = cnt;
}
if (top)
{
int t = 0;
int k = 0;
while (1 << k < (top - 1)) k++;
for (int j = k; ~j; j--) if (t + (1 << j) < top)
{
if (slope(stack[t + (1 << j)], stack[t + (1 << j) - 1]) < tb.d)
t += 1 << j;
}
f[idx[i]] = std::max(f[idx[i]], DP2(idx[i], stack[t]));
}
}
for (int i = 1; i <= m; i++)
printOut(f[i]);
}
int main()
{
#ifndef LOCAL
freopen("gas.in", "r", stdin);
freopen("gas.out", "w", stdout);
#endif
run();
return 0;
}