题解 P10196【[USACO24FEB] Lazy Cow P】
总算铂金组场切一题。似乎做麻烦了,而且常数蛮大的,但是没啥思维难度,记录一下。
对于每一个需求,我们将其放置在平面直角坐标系的 \((m_i,b_i)\) 位置。另外,自然有一个 \((0,0)\) 需求,也同样处理这个点。我们需要支持插入一个点的操作,并维护出答案。
先考虑不需要动态插点,只在最后求一次答案怎么做。鉴于代价呈指数函数增长,一个基础的思路就是考虑所有“最严格”的需求,并将所有要完成的工作尽量平均地分给截止前的每一时刻。接下来的问题就是如何判定“最严格”。
考虑下图中的需求:(横纵坐标不是整数,但这不重要)
现有 \(A,B,C,D,E,F,P\) 七个需求,那么直观上 \(P\) 需求就不是“最严格”的,因为 \(D\) 需求比 \(C\) 需求多出来的工作量会被平均分到 \(h\) 线段上,而这样就自然满足了 \(P\) 需求。容易证明,一个需求是“最严格”的,当且仅当它在所有需求组成的上凸壳内。假如不需要动态插点,维护出上凸壳并计算出每一个线段分别的代价,再加起来即可。
假如在上面的基础上加入 \(Q\) 需求,则 \(C,D\) 需求就不再“最严格”了,将它们弹出并插入 \(Q\) 需求即可。上述过程可以使用平衡树维护。代码中平衡树节点的 \(m,b\) 即为需求的坐标,\(E\) 是当前需求和上凸壳中前一个需求之间的线段对应的代价,\(sumE\) 是子树内 \(E\) 的和。在实现时,只需要弹出不再“最严格”的所有需求,再插入当前需求,最后重新计算当前需求及其后继(如果存在)的 \(E\) 即可。
时间复杂度 \(O(D\log D)\)。
免责声明:这是我第一次写平衡树维护凸包,是边口胡边写加上 debug 好久出来的产物,因此代码可能较为丑陋,请谨慎参考。
//By: OIer rui_er
#include <bits/stdc++.h>
#define rep(x, y, z) for(int x = (y); x <= (z); ++x)
#define per(x, y, z) for(int x = (y); x >= (z); --x)
#define debug(format...) fprintf(stderr, format)
#define fileIO(s) do {freopen(s".in", "r", stdin); freopen(s".out", "w", stdout);} while(false)
#define endl '\n'
using namespace std;
typedef long long ll;
mt19937 rnd(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
int randint(int L, int R) {
uniform_int_distribution<int> dist(L, R);
return dist(rnd);
}
template<typename T> void chkmin(T& x, T y) {if(x > y) x = y;}
template<typename T> void chkmax(T& x, T y) {if(x < y) x = y;}
template<int mod>
inline unsigned int down(unsigned int x) {
return x >= mod ? x - mod : x;
}
template<int mod>
struct Modint {
unsigned int x;
Modint() = default;
Modint(unsigned int x) : x(x) {}
friend istream& operator>>(istream& in, Modint& a) {return in >> a.x;}
friend ostream& operator<<(ostream& out, Modint a) {return out << a.x;}
friend Modint operator+(Modint a, Modint b) {return down<mod>(a.x + b.x);}
friend Modint operator-(Modint a, Modint b) {return down<mod>(a.x - b.x + mod);}
friend Modint operator*(Modint a, Modint b) {return 1ULL * a.x * b.x % mod;}
friend Modint operator/(Modint a, Modint b) {return a * ~b;}
friend Modint operator^(Modint a, ll b) {Modint ans = 1; for(; b; b >>= 1, a *= a) if(b & 1) ans *= a; return ans;}
friend Modint operator~(Modint a) {return a ^ (mod - 2);}
friend Modint operator-(Modint a) {return down<mod>(mod - a.x);}
friend Modint& operator+=(Modint& a, Modint b) {return a = a + b;}
friend Modint& operator-=(Modint& a, Modint b) {return a = a - b;}
friend Modint& operator*=(Modint& a, Modint b) {return a = a * b;}
friend Modint& operator/=(Modint& a, Modint b) {return a = a / b;}
friend Modint& operator^=(Modint& a, ll b) {return a = a ^ b;}
friend Modint& operator++(Modint& a) {return a += 1;}
friend Modint operator++(Modint& a, int) {Modint x = a; a += 1; return x;}
friend Modint& operator--(Modint& a) {return a -= 1;}
friend Modint operator--(Modint& a, int) {Modint x = a; a -= 1; return x;}
friend bool operator==(Modint a, Modint b) {return a.x == b.x;}
friend bool operator!=(Modint a, Modint b) {return !(a == b);}
};
typedef Modint<1000000007> mint;
const ll N = 5e5 + 5;
map<ll, ll> now;
inline bool check(ll m1, ll b1, ll m2, ll b2, ll m3, ll b3) {
ll dm1 = m2 - m1, db1 = b2 - b1;
ll dm2 = m3 - m2, db2 = b3 - b2;
return (__int128)db1 * dm2 > (__int128)db2 * dm1;
}
inline mint calc(ll m1, ll b1, ll m2, ll b2) {
ll dm = m2 - m1, db = b2 - b1;
if(db < dm) return db;
mint k = mint(3) ^ (db / dm - 1);
return (db % dm) * (k * 3) + (dm - db % dm) * k;
}
struct Node {
ll m, b, rnd, sz, lc, rc;
mint E, sumE;
Node(ll m = 0, ll b = 0, mint E = 0, ll sz = 0) : m(m), b(b), E(E), sumE(E), rnd(rand()), sz(sz), lc(0), rc(0) {}
};
struct FHQTreap {
Node t[N];
ll sz, rt;
void pushup(ll u) {
t[u].sz = t[t[u].lc].sz + t[t[u].rc].sz + 1;
t[u].sumE = t[t[u].lc].sumE + t[t[u].rc].sumE + t[u].E;
}
ll newnode(ll m, ll b, mint E) {
t[++sz] = Node(m, b, E, 1);
return sz;
}
void split(ll u, ll lim, ll& x, ll& y) {
if(!u) {x = y = 0; return;}
if(t[u].m <= lim) {
x = u;
split(t[u].rc, lim, t[u].rc, y);
}
else {
y = u;
split(t[u].lc, lim, x, t[u].lc);
}
pushup(u);
}
ll merge(ll u, ll v) {
if(!u || !v) return u | v;
if(t[u].rnd < t[v].rnd) {
t[u].rc = merge(t[u].rc, v);
pushup(u);
return u;
}
else {
t[v].lc = merge(u, t[v].lc);
pushup(v);
return v;
}
}
ll merge(ll u, ll v, ll args...) {
return merge(merge(u, v), args);
}
void insert(ll m, ll b, mint E) {
ll L, R;
split(rt, m, L, R);
rt = merge(L, newnode(m, b, E), R);
}
void erase(ll m) {
ll L, M, R;
split(rt, m - 1, L, R);
split(R, m, M, R);
M = merge(t[M].lc, t[M].rc);
rt = merge(L, M, R);
}
ll kth(ll u, ll k) {
if(k <= 0 || k > t[u].sz) return 0;
while(true) {
if(t[t[u].lc].sz >= k) {u = t[u].lc; continue;}
if(t[t[u].lc].sz + 1 == k) return u;
k -= t[t[u].lc].sz + 1;
u = t[u].rc;
}
}
ll pre(ll m) {
ll L, R;
split(rt, m - 1, L, R);
ll ans = kth(L, t[L].sz);
rt = merge(L, R);
return ans;
}
ll suc(ll m) {
ll L, R;
split(rt, m, L, R);
ll ans = kth(R, 1);
rt = merge(L, R);
return ans;
}
void print(ll u) {
if(t[u].lc) print(t[u].lc);
cout << "(" << t[u].m << ", " << t[u].b << ", " << t[u].E << ", " << t[u].sumE << ")" << endl;
if(t[u].rc) print(t[u].rc);
}
}fhq;
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
fhq.insert(0, 0, 0);
now[0] = 0;
ll n, m, b;
for(cin >> n; n; --n) {
cin >> m >> b;
if(now[m] < b) {
ll p = fhq.pre(m);
if(fhq.t[p].b < b) {
if(now[m]) {
fhq.erase(m);
now[m] = 0;
}
// cout << "P " << p << " " << fhq.t[p].m << " " << fhq.t[p].b << " " << m << " " << b << endl << flush;
ll q = fhq.pre(fhq.t[p].m);
// cout << "? " << q << endl << flush;
while(q) {
// cout << "Q " << q << endl << flush;
if(!check(fhq.t[q].m, fhq.t[q].b, fhq.t[p].m, fhq.t[p].b, m, b)) {
fhq.erase(fhq.t[p].m);
now[fhq.t[p].m] = 0;
p = q;
q = fhq.pre(fhq.t[p].m);
}
else break;
}
// cout << "?" << endl << flush;
ll x = fhq.suc(m);
// cout << "X " << x << " " << fhq.t[x].m << " " << fhq.t[x].b << " " << m << " " << b << endl << flush;
while(x) {
if(fhq.t[x].b <= b) {
fhq.erase(fhq.t[x].m);
now[fhq.t[x].m] = 0;
x = fhq.suc(m);
}
else break;
}
// cout << "X " << x << " " << fhq.t[x].m << " " << fhq.t[x].b << " " << m << " " << b << endl << flush;
if(x) {
ll y = fhq.suc(fhq.t[x].m);
while(y) {
// cout << "Y " << y << endl << flush;
if(!check(m, b, fhq.t[x].m, fhq.t[x].b, fhq.t[y].m, fhq.t[y].b)) {
fhq.erase(fhq.t[x].m);
now[fhq.t[x].m] = 0;
x = y;
y = fhq.suc(fhq.t[x].m);
}
else break;
}
fhq.erase(fhq.t[x].m);
fhq.insert(fhq.t[x].m, fhq.t[x].b, calc(fhq.t[p].m, fhq.t[p].b, fhq.t[x].m, fhq.t[x].b));
}
if(!x || check(fhq.t[p].m, fhq.t[p].b, m, b, fhq.t[x].m, fhq.t[x].b)) {
if(x) {
fhq.erase(fhq.t[x].m);
fhq.insert(fhq.t[x].m, fhq.t[x].b, calc(m, b, fhq.t[x].m, fhq.t[x].b));
}
fhq.insert(m, b, calc(fhq.t[p].m, fhq.t[p].b, m, b));
now[m] = b;
}
}
}
// fhq.print(fhq.rt);
cout << fhq.t[fhq.rt].sumE << endl;
// cout << flush;
}
return 0;
}