1 条题解

  • 0
    @ 2026-5-14 18:06:34

    k=1k=1 时,新的凸包必然来源于原来的凸包,或者剥掉原来凸包后新的点集的凸包。

    递归地做这个事情,如果最上面一层没有点没删,则停止递归,否则处理出下面一层的凸包后,处理出删掉这层的点后会露出的部分,将这一部分从下面分开来,连到上面即可。由于每层都至少要删一个点,所以只要预处理 k+1k+1 层的凸包即可。

    把凸包分成上下凸壳,并钦定一边可以平行于 yy 轴,面积即为上下凸壳的面积和。用线段树维护每一层的凸壳,pushup 的时候加上中间的面积。

    找露出的部分,即找点到凸包的切线,可以二分。取中间的线段,判断方向后往对应方向递归即可。左右切到一个点的时候要特判凸性。

    剪切,拼接的部分可以直接用线段树分裂和合并,由于对应的 xx 坐标是不交的,所以每次操作复杂度都是严格 O(logn)O(\log n)。为了处理多次询问,可以用可持久化的思路,不对原来的凸壳做实质性修改,询问完毕后将多产生的点直接删除即可。

    复杂度 O(nk+(n+k)logn)O(nk+(n+\sum k)\log n)

    如果在 uoj 上 97 分,检查有没有判:

    • 输入 cic_i 加上 SS 爆 int;
    • 输入 cic_i00
    • 输入 cic_i 解密后有重复。

    代码:https://uoj.ac/submission/653541

    /* name: P3827
     * author: 5ab
     * created at: 2023-09-06
     */
    #pragma GCC optimize("Ofast","unroll-loops")
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx")
    #include <iostream>
    #include <algorithm>
    #include <numeric>
    #include <cassert>
    #include <vector>
    #include <tuple>
    using namespace std;
    
    #define all(x) (x).begin(), (x).end()
    #define ssz(x) (int((x).size()))
    
    auto chmax = [](auto& x, auto y) { if (x < y) x = y; };
    auto chmin = [](auto& x, auto y) { if (x > y) x = y; };
    
    using ll = long long;
    const int max_n = 1e5, max_lgn = 17, max_k = 101, max_s = (max_lgn + 1) * 2 * (max_n + 5 * max_k);
    
    struct point
    {
    	int x, y;
    	point operator-(const point& rhs) const {
    		return point{ x - rhs.x, y - rhs.y };
    	}
    	bool operator<(const point& rhs) const {
    		return x == rhs.x ? y < rhs.y : x < rhs.x;
    	}
    };
    inline ll cross(const point& p, const point& q) {
    	return 1ll * p.x * q.y - 1ll * p.y * q.x;
    }
    
    struct node
    {
    	int ls, rs, lx, rx, siz;
    	ll sm;
    }
    tr[max_s];
    int ind = 0, n;
    
    int nnode() { return ind++; }
    int clone(int x)
    {
    	tr[ind] = tr[x];
    	return ind++;
    }
    
    inline int glx(int x) { return tr[x].lx; }
    inline int grx(int x) { return tr[x].rx; }
    inline ll gsm(int x) { return tr[x].sm; }
    inline int gsz(int x) { return x == -1 ? 0 : tr[x].siz; }
    
    vector<int> cv;
    struct Hull
    {
    	point a[max_n];
    	int ord[max_n], cl[max_n];
    	vector<int> rt;
    	
    	void pushup(int id)
    	{
    		if (tr[id].ls == -1)
    		{
    			int tmp = tr[id].rs;
    			tr[id] = tr[tr[id].rs];
    			tr[id].ls = -1, tr[id].rs = tmp;
    		}
    		else if (tr[id].rs == -1)
    		{
    			int tmp = tr[id].ls;
    			tr[id] = tr[tr[id].ls];
    			tr[id].rs = -1, tr[id].ls = tmp;
    		}
    		else
    		{
    			tr[id].lx = glx(tr[id].ls), tr[id].rx = grx(tr[id].rs);
    			// cerr << id << " " << glx(tr[id].rs) << " + " << grx(tr[id].ls) << " "
    			   //   << cross(a[glx(tr[id].rs)], a[grx(tr[id].ls)]) << " " << gsm(tr[id].ls) << " " << gsm(tr[id].rs) << endl;
    			tr[id].sm = gsm(tr[id].ls) + gsm(tr[id].rs) + cross(a[glx(tr[id].rs)], a[grx(tr[id].ls)]);
    			tr[id].siz = gsz(tr[id].ls) + gsz(tr[id].rs);
    		}
    	}
    	
    	int build(int l, int r, int ql, int qr)
    	{
    		if (ql >= qr)
    			return -1;
    		int id = nnode();
    		if (l == r)
    		{
    			tr[id] = { -1, -1, l, l, 1, 0 };
    			return id;
    		}
    		int mid = (l + r) >> 1, dfx = upper_bound(all(cv), mid) - begin(cv);
    		tr[id].ls = build(l, mid, ql, dfx);
    		tr[id].rs = build(mid + 1, r, dfx, qr);
    		pushup(id);
    		// cerr << id << " " << l << " " << r << " " << ssz(cv) << " " << tr[id].ls << " " << tr[id].rs << endl;
    		return id;
    	}
    	
    	void init(point *s)
    	{
    		copy(s, s + n, a);
    		sort(a, a + n);
    		fill(cl, cl + n, -1);
    		for (int i = 0; i < n; i++)
    			ord[i] = lower_bound(a, a + n, s[i]) - a;
    		
    		for (int _ = 0; _ <= max_k; _++)
    		{
    			cv.clear();
    			for (int i = 0; i < n; i++) if (cl[i] == -1)
    			{
    				while (ssz(cv) > 1 && cross(a[cv.back()] - a[end(cv)[-2]], a[i] - a[cv.back()]) > 0)
    					cv.pop_back();
    				cv.push_back(i);
    			}
    			for (int x : cv)
    				cl[x] = _;
    			if (cv.empty())
    				break;
    			rt.push_back(build(0, n - 1, 0, ssz(cv)));
    			// cerr << _ << " " << rt[_] << ":  ";
    			// for (int x : cv)
    			// 	cerr << x << " ";
    			// cerr << endl;
    		}
    	}
    	
    	int findl(int sx, int id)
    	{
    		int l = 0, r = n - 1;
    		while (l < r)
    		{
    			int mid = (l + r) >> 1;
    			if (tr[id].ls == -1)
    				id = tr[id].rs, l = mid + 1;
    			else if (tr[id].rs == -1)
    				id = tr[id].ls, r = mid;
    			else if (glx(tr[id].rs) > sx || cross(a[glx(tr[id].rs)] - a[grx(tr[id].ls)], a[sx] - a[grx(tr[id].ls)]) > 0)
    				id = tr[id].ls, r = mid;
    			else
    				id = tr[id].rs, l = mid + 1;
    		}
    		// cerr << sx << " " << id << " " << l << endl;
    		return l > sx ? -1 : l;
    	}
    	int findr(int sx, int id)
    	{
    		int l = 0, r = n - 1;
    		while (l < r)
    		{
    			int mid = (l + r) >> 1;
    			// cerr << "findr: " << sx << " " << id << " " << l << " " << r << endl;
    			if (tr[id].ls == -1)
    				id = tr[id].rs, l = mid + 1;
    			else if (tr[id].rs == -1)
    				id = tr[id].ls, r = mid;
    			else if (grx(tr[id].ls) < sx || cross(a[grx(tr[id].ls)] - a[glx(tr[id].rs)], a[sx] - a[glx(tr[id].rs)]) < 0)
    				id = tr[id].rs, l = mid + 1;
    			else
    				id = tr[id].ls, r = mid;
    		}
    		return l < sx ? n : l;
    	}
    	
    	int getrk(int x, int id)
    	{
    		int l = 0, r = n - 1, csiz = 0;
    		while (l < r)
    		{
    			int mid = (l + r) >> 1;
    			if (x <= mid)
    				r = mid, id = tr[id].ls;
    			else
    				csiz += gsz(tr[id].ls), l = mid + 1, id = tr[id].rs;
    		}
    		return csiz;
    	}
    	int fndbyrk(int rk, int id)
    	{
    		// cerr << "fndrk: " << rk << " " << tr[id].siz << endl;
    		int l = 0, r = n - 1;
    		while (l < r)
    		{
    			int mid = (l + r) >> 1;
    			if (gsz(tr[id].ls) > rk)
    				r = mid, id = tr[id].ls;
    			else
    				l = mid + 1, rk -= gsz(tr[id].ls), id = tr[id].rs;
    		}
    		return l;
    	}
    	
    	int split(int &x, int L, int R, int l, int r)
    	{
    		// cerr << "split: " << x << " " << L << " " << R << " " << l << " " << r << endl;
    		if (x == -1)
    			return -1;
    		if (L <= l && r <= R)
    		{
    			int tmp = x; x = -1;
    			return tmp;
    		}
    		// cerr << tr[x].ls << " " << tr[x].rs << endl;
    		x = clone(x);
    		int mid = (l + r) >> 1, yl = -1, yr = -1;
    		if (L <= mid)
    			yl = split(tr[x].ls, L, R, l, mid);
    		if (mid < R)
    			yr = split(tr[x].rs, L, R, mid + 1, r);
    		if (yl == -1 && yr == -1)
    			return -1;
    		int y;
    		if (tr[x].ls == -1 && tr[x].rs == -1)
    			y = x, x = -1;
    		else
    			y = nnode(), pushup(x);
    		tr[y].ls = yl, tr[y].rs = yr;
    		pushup(y);
    		// cerr << l << " " << r << ": " << x << " " << y << endl;
    		return y;
    	}
    	
    	void merge(int &x, int y)
    	{
    		if (x == -1 || y == -1)
    		{
    			x = x + y + 1;
    			return;
    		}
    		x = clone(x);
    		merge(tr[x].ls, tr[y].ls);
    		merge(tr[x].rs, tr[y].rs);
    		pushup(x);
    	}
    	
    	ll solve(vector<int> dc)
    	{
    		for (int &x : dc)
    			x = ord[x];
    		sort(all(dc), [&](int x, int y) {
    			return cl[x] == cl[y] ? x < y : cl[x] > cl[y];
    		});
    		
    		int st = ind;
    		vector<int> nrt(size(rt));
    		for (int i = 0; i < ssz(rt); i++)
    			nrt[i] = clone(rt[i]);
    		
    		for (int x : dc) if (cl[x] != -1)
    		{
    			int &xt = nrt[cl[x]];
    			int crk = getrk(x, xt), lp = 0, rp = n - 1, px = -1;
    			// cerr << x << " " << cl[x] << " " << xt << " " << gsz(xt) << endl;
    			// cerr << crk << endl;
    			if (cl[x] < ssz(rt) - 1 && nrt[cl[x] + 1] != -1)
    			{
    				int pre = -1, nxt = -1;
    				int &yt = nrt[cl[x] + 1];
    				if (crk > 0)
    					lp = findr(pre = fndbyrk(crk - 1, xt), yt);
    				if (crk < gsz(xt) - 1)
    					rp = findl(nxt = fndbyrk(crk + 1, xt), yt);
    				// cerr << pre << " " << nxt << " " << lp << " " << rp << " " << cross(a[nxt] - a[lp], a[lp] - a[pre]) << endl;
    				if (lp < rp || (lp == rp && (pre == -1 || nxt == -1 || cross(a[nxt] - a[lp], a[lp] - a[pre]) >= 0)))
    				{
    					// cerr << "split " << yt << " into: " << lp << " " << rp << endl;
    					px = split(yt, lp, rp, 0, n - 1);
    				}
    			}
    			split(xt, x, x, 0, n - 1);
    			merge(xt, px);
    		}
    		
    		ll ans = tr[nrt[0]].sm;
    		ind = st;
    		return ans;
    	}
    }
    U, D;
    
    point a[max_n];
    
    signed main()
    {
    	ios_base::sync_with_stdio(false);
    	cin.tie(nullptr);
    	
    	int m;
    	
    	cin >> n >> m;
    	for (int i = 0; i < n; i++)
    		cin >> a[i].x >> a[i].y;
    	
    	U.init(a);
    	for (int i = 0; i < n; i++)
    		a[i].x *= -1, a[i].y *= -1;
    	D.init(a);
    	
    	// cerr << clock() << endl;
    	
    	vector<int> ps;
    	int k, lastans = -1;
    	
    	while (m--)
    	{
    		cin >> k;
    		ps.resize(k);
    		for (int i = 0; i < k; i++)
    		{
    			cin >> ps[i];
    			ps[i] = (1ll * ps[i] + lastans + n) % n;
    			// cerr << ps[i] << endl;
    		}
    		sort(all(ps));
    		ps.erase(unique(all(ps)), end(ps));
    		ll ans = U.solve(ps) + D.solve(ps);
    		cout << ans << "\n";
    		lastans = ans % n;
    	}
    	
    	return 0;
    }
    // started coding at: 09-06 09:13:03
    
    • 1

    信息

    ID
    6616
    时间
    3000ms
    内存
    768MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者