首页 > 其他分享 >题解 P10196【[USACO24FEB] Lazy Cow P】

题解 P10196【[USACO24FEB] Lazy Cow P】

时间:2024-02-28 19:12:09浏览次数:20  
标签:sz return 题解 ll P10196 operator Modint USACO24FEB friend

总算铂金组场切一题。似乎做麻烦了,而且常数蛮大的,但是没啥思维难度,记录一下。

对于每一个需求,我们将其放置在平面直角坐标系的 \((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;
}

标签:sz,return,题解,ll,P10196,operator,Modint,USACO24FEB,friend
From: https://www.cnblogs.com/ruierqwq/p/18041476/LG-P10196

相关文章

  • 第十四届蓝桥杯个人题解
    a幸运的数主要是思路 遍历1-100000000每一层循环,首先将其每一位分到数组里,并记录位数,如果是偶数位再接着往下,比较前半和后半是否相等:通过加减最后结果是否为零来判断intmain(){  intnum=0;  for(inti=1;i<100000000;i++)  {    ints[9]={0};......
  • ABC338G evall 题解
    题意:给定一个由数字和加号和乘号组成的字符串,求出\(\sums(i,j)\)。其中\(s(i,j)\)表示\(i\)到\(j\)字符组成的表达式的值。考虑\(\text{dp}\)。设\(dp_{i}\)表示以\(i\)为起点的所有表达式的值之和。那么我们考虑以一些加号作为分界点来转移。假设\(i\)右边最......
  • CF1209G2 Into Blocks (hard version) 题解
    Description给你\(n\),\(q\),\(n\)表示序列长度,\(q\)表示操作次数。我们需要达成这么一个目标状态:如果存在\(x\)这个元素,那么必须满足所有\(x\)元素都必须在序列中连续。然后你可以进行这么一种操作,将所有的\(x\)元素的变为任意你指定的\(y\)元素,并且花费\(cnt[x......
  • 2024牛客寒假算法基础集训营6 题解 ( A,B,C,D,E,I)
    2024牛客寒假算法基础集训营6题解(A,B,C,D,E,I)A 宇宙的终结题意找到\([l,r]\)区间中有多少数恰好等于三个不同素数的乘积思路数据范围很小,可以考虑暴力,遍历\([l,r]\)区间内每个数,拿每个小于当前数的素数一直往下除,判断是否存在能被恰好3个素数整除的情况代码/********......
  • 题解 NKOJ2929 【[THUSC2014] 函数求解】
    代码:#include<iostream>#include<queue>#include<cstdio>#include<cmath>usingnamespacestd;typedefstruct{ intnxt; intend; intdis; doublecost;}Edge;constintN=2e3,M=400+7,K=80800+7;constdoubleep......
  • ABC294 EFG 题解
    E-2xNGrid题意给你一个\(2\timesL\)的网格,但是\(L\)很大,所以用以下形式压缩:将同一个颜色的连续段视为一个整体,那么每一行就可以用若干个二元组\((a_i,b_i)\)表示,其中\(a_i\)为颜色,\(b_i\)为连续段的长度。保证长度\(\le10^5\)。输入以上述形式压缩,现在让你求出......
  • P1110 [ZJOI2007] 报表统计 题解
    考察点:STL的熟练运用。记:\(l_i\)表示序列中不超过\(a_i\)的最大数,\(r_i\)表示序列中超过\(a_i\)的最小数。开一个vectorinsert[N]存储\(a_i\)后面插入的所有数字。首先,我们使用一个multisets1来存储相邻元素的差的绝对值,然后再开一个multisets2来存储当前出......
  • CodeChef Chef and Churus 题解
    对给出的\(n\)个区间分块,设块长为\(B\)。每个块内计算每个位置的贡献(被覆盖次数)。具体地,记\(f_{i,j}\)表示第\(i\)个块第\(j\)个数被覆盖了多少次,这个可以用差分轻松维护。预处理复杂度\(O(\frac{n^2}{B})\)。现在考虑修改。记\(ans_i\)表示块\(i\)的贡献,那么对于......
  • ABC303 G 题解
    区间DP。设\(f_{l,r}\)表示只考虑\([l,r]\),先手得分减后手得分的最大值(并不关心谁是先手谁是后手),区间长度\(len=r-l+1\)。然后对三种情况分别讨论:使用操作一,此时取掉左/右端点的部分先手后手互换,对答案的贡献为\(\max(a_l-f_{l+1,r},a_r-f_{l,r-1})\)。使用操作二,继......
  • P1668 题解
    两种做法。一、最短路题目要求区间数量最小。如果能建出图来,就可以转换为最短路问题。具体地,我们从\(l-1\tor\)连一条长度为\(1\)的边,意味着要多经过\((l-1,r]\)这一个区间。这是左开右闭的形式。现在还有一个问题:通过这种边我们只能到达区间的右端点,如果想向左到达区......