JOI2012 魚(Fish) 题解

发布时间 2023-07-21 16:47:30作者: 喵仔牛奶

Description

给定 \(n\) 条鱼,每条鱼有长度和颜色。你可以选出若干条鱼,需要满足最大长度小于最小长度的两倍。定义两种养鱼方案不同仅当它们三种颜色之一的出现次数不同,求不同的养鱼方案数。

对于 \(100\%\) 的数据,\(1\leq n\leq 5\times 10^5\)

题目链接:JOIAtCoder洛谷

Solution

先按长度排序,然后双指针,找出所有极长合法子区间。设一个极长合法子区间红、绿、蓝三种颜色的鱼分别有 \(R,G,B\) 条,则三种颜色出现次数为 \((x,y,z)\)\(x\leq R,y\leq G,z\leq B\) 的方案是合法的,因为你可以去掉一些鱼。

问题转化为给定一些 \((0\sim x,0\sim y,0\sim z)\) 的立方体,求体积并。

先对 \(z\) 那维扫描线,问题变成二维矩阵求面积并。乍一看十分不可做,但问题的关键在于下界为 \((0,0,0)\)。观察发现,面积并一定为阶梯型

于是将其变为一维的序列,\(x\) 为下标,\(y\) 为值,根据上文的结论,序列单调不升。每次加入矩阵即将 \([1,x]\) 的值对 \(y\)\(\max\)。可以以 \(x\)\(R\),找出第一个值 \(< y\) 的位置作为 \(L\),将 \([L,R]\) 全部赋值为 \(y\)

使用线段树二分容易在 \(\log n\) 的时间内完成一次加入,但我们可以用珂朵莉树更简单地完成。由于 \(R\) 已知,只需向左枚举删除的区间直到区间的值 \(\geq y\) 为止。其实,也可以用 set 只维护右端点,也十分简便。

时间复杂度 \(\mathcal{O}(n\log n)\)

Code

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
namespace Milkcat {
	typedef long long LL;
    typedef pair<LL, char> pic;
	const int N = 1e6 + 5;
	struct node {
        LL l, r; mutable LL v;
        node(int L, int R = -1, int V = 0): l(L), r(R), v(V) {}
        bool operator < (const node& o) const { return l < o.l; }
        int size() const { return r - l + 1; }
    };
    typedef set<node>::iterator IT;
    LL n, ans, now, col[N]; pic a[N];
    vector<node> vec; set<node> s;
    IT split(int pos) {
        IT it = s.lower_bound(node(pos));
        if (it != s.end() && it->l == pos) return it;
        int L = (-- it)->l, R = it->r, V = it->v;
        s.erase(it), s.insert(node(L, pos - 1, V));
        return s.insert(node(pos, R, V)).first;
    }
    void assign(LL r, LL val = 0) {
        LL l = 0;
        for (IT it = prev(split(r + 1)); it->v < val; -- it) {
        	l = it->l, now -= it->v * it->size();
			if (it == s.begin()) break;
		}
		if (!l) return;
		IT itr = split(r + 1), itl = split(l);
		s.erase(itl, itr), s.insert(node(l, r, val)), now += val * (r - l + 1);
    }
    int main() {
    	cin >> n;
    	for (int i = 1; i <= n; i ++)
    		cin >> a[i].fi >> a[i].se;
    	sort(a + 1, a + 1 + n);
    	for (int l = 1, r = 0; l <= n; col[a[l].se] --, l ++) {
    		while (r + 1 <= n && a[r + 1].fi < a[l].fi * 2) r ++, col[a[r].se] ++;
    		vec.emplace_back(col['R'] + 1, col['G'] + 1, col['B'] + 1);
		}
		sort(vec.begin(), vec.end()), reverse(vec.begin(), vec.end());
		vec.push_back(node(0, 0, 0)), s.insert(node(1, n + 1, 0));
		for (int i = 0; i + 1 < vec.size(); i ++) {
			int x = vec[i].r, y = vec[i].v;
			assign(x, y), ans += now * (vec[i].l - vec[i + 1].l);
		}
		cout << ans - 1 << '\n';
        return 0;
    }
}
int main() {
	freopen("memory.in", "r", stdin);
	freopen("memory.out", "w", stdout);
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
    int T = 1;
    while (T --) Milkcat::main();
    return 0;
}