题解 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;
}
posted @ 2024-02-28 19:05  rui_er  阅读(20)  评论(0编辑  收藏  举报