Description

给定序列 a = ( a 1 , a 2 , ⋯   , a n ) a=(a_1,a_2,\cdots,a_n) a=(a1,a2,,an),有 m m m 个操作分两种:

  • add ⁡ ( l , r , x ) \operatorname{add}(l,r,x) add(l,r,x):对每个 i ∈ [ l , r ] i\in[l,r] i[l,r] 执行 a i ← a i + x a_i\gets a_i+x aiai+x.
  • kth ⁡ ( l , r , k ) \operatorname{kth}(l,r,k) kth(l,r,k):求 a l ⋯ r a_{l\cdots r} alr 中第 k k k 小的值,无解输出 − 1 -1 1.

Limitations

1 ≤ n , m ≤ 1 0 5 1\le n,m\le 10^5 1n,m105
∣ a i ∣ , ∣ x ∣ ≤ 2 × 1 0 4 |a_i|,|x| \le 2\times 10^4 ai,x2×104
2 s , 128 MB 2\text{s},128\text{MB} 2s,128MB

Solution

看到这种很难维护的操作,考虑分块,每块进行升序排序后存入 p i p_i pi.
先考虑 kth ⁡ \operatorname{kth} kth,先取 a l ⋯ r a_{l\cdots r} alr 中的最小值和最大值.
k = 1 k=1 k=1,答案为最小值,若 k = r − l + 1 k=r-l+1 k=rl+1,答案为最大值,若超出范围则为 − 1 -1 1,否则以最小值和最大值为左右端点,进行二分.
对于 check ⁡ \operatorname{check} check,求出区间内不大于 mid \textit{mid} mid 的数的个数,具体就是散块暴力,整块在 p i p_i pi 上二分,外层二分出的结果就是答案.
再考虑 add ⁡ \operatorname{add} add,显然是整块打上 tag \textit{tag} tag,散块重排.

需要注意几点:

  • 计算时不要漏掉标记.
  • 需要开 long long,否则会被 hack.
  • 重排时不要直接 sort,要分成两组(加过的和没加过的)再归并.
  • 加快读快写, B ∈ [ 200 , 300 ] B\in[200,300] B[200,300] 最优.

Code

4.48 KB , 11.20 s , 3.92 MB    (in   total,   C++20   with   O2) 4.48\text{KB},11.20\text{s},3.92\text{MB}\;\texttt{(in total, C++20 with O2)} 4.48KB,11.20s,3.92MB(in total, C++20 with O2)
代码中 B B B 256 256 256.

#include <bits/stdc++.h>
using namespace std;

using i64 = long long;
using ui64 = unsigned long long;
using i128 = __int128;
using ui128 = unsigned __int128;
using f4 = float;
using f8 = double;
using f16 = long double;

template<class T>
bool chmax(T &a, const T &b){
	if(a < b){ a = b; return true; }
	return false;
}

template<class T>
bool chmin(T &a, const T &b){
	if(a > b){ a = b; return true; }
	return false;
}

// Fast-io
#ifdef LOCAL
#define getchar_unlocked getchar
#define putchar_unlocked putchar
#endif

template<class T>
inline T read() {
    T x = 0, f = 1;
    char ch = getchar_unlocked();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar_unlocked();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar_unlocked();
    }
    return x * f;
}

template<class T>
inline void write(T x) {
    if (x < 0) {
        putchar_unlocked('-');
        x = -x;
    }
    if (x > 9) write(x / 10);
    putchar_unlocked(x % 10 + '0');
    return;
}

constexpr int B = 256;      // Size of each block.
constexpr i64 inf = 6e18;
struct Node {
	i64 val;
	int pos;
	inline Node() {}
	inline Node(i64 _val, int _pos) : val(_val), pos(_pos) {}
	bool operator<(const Node& rhs) const { return val < rhs.val; }
};

struct Block {
	int n, blocks;
	vector<i64> a, tag;
	vector<int> belong, L, R;
	vector<Node> p, t1, t2;
	inline Block() {}
	inline Block(const vector<i64>& _a) : a(_a) {
		n = a.size();
		blocks = (n + B - 1) / B;
		p.resize(n), belong.resize(n);
		L.resize(blocks), R.resize(blocks), tag.resize(blocks);
		// Build blocks
		for (int i = 0; i < blocks; i++) {
			L[i] = i * B;
			R[i] = min(L[i] + B, n) - 1;
			for (int j = L[i]; j <= R[i]; j++) {
				belong[j] = i;
				p[j] = Node(a[j], j);
			}
			sort(p.begin() + L[i], p.begin() + R[i] + 1);   // Sort for each block.
		}
	}
	
	// Find [min, max] of range [l, r].
	inline pair<i64, i64> bound(int l, int r) {
		int bl = belong[l], br = belong[r];
		i64 vl = inf, vr = -inf;
		if (bl == br) {
			for (int i = l; i <= r; i++) {
				vl = min(vl, a[i] + tag[bl]);
				vr = max(vr, a[i] + tag[bl]);
			}
		}
		else {
			for (int i = l; i <= R[bl]; i++) {
				vl = min(vl, a[i] + tag[bl]);
				vr = max(vr, a[i] + tag[bl]);
			}
			for (int i = L[br]; i <= r; i++) {
				vl = min(vl, a[i] + tag[br]);
				vr = max(vr, a[i] + tag[br]);
			}
			for (int i = bl + 1; i < br; i++) {
				vl = min(vl, p[L[i]].val + tag[i]);
				vr = max(vr, p[R[i]].val + tag[i]);
			}
		}
		return {vl, vr};
	}
	
	// Find the number of elements LEQ k in range [l, r].
	inline int count(int l, int r, int k) {
		int res = 0, bl = belong[l], br = belong[r];
		if (bl == br) {
			for (int i = l; i <= r; i++) res += (a[i] + tag[bl] <= k);
		}
		else {
			for (int i = l; i <= R[bl]; i++) res += (a[i] + tag[bl] <= k);
			for (int i = L[br]; i <= r; i++) res += (a[i] + tag[br] <= k);
			for (int i = bl + 1; i < br; i++) {
				int vl = L[i], vr = R[i];
				if (p[vl].val + tag[i] > k) continue;
				if (p[vr].val + tag[i] <= k) {
					res += vr - vl + 1;
					continue;
				}
				while (vl < vr) {
					int mid = ((vl + vr) >> 1) + 1;
					if (p[mid].val + tag[i] <= k) vl = mid;
					else vr = mid - 1;
				}
				if (p[vl].val + tag[i] <= k) res += vl - L[i] + 1;
			}
		}
		return res;
	}
	
	// Find the k-th smallest number in range [l, r].
	inline i64 kth(int l, int r, int k) {
		auto [vl, vr] = bound(l, r);
		const int len = r - l + 1;
		if (k == 1) return vl;
		if (k == len) return vr;
		if (k < 1 || k > len) return -1;
		i64 res = -1;
		while (vl <= vr) {
			i64 mid = (vl + vr) >> 1;
			if (count(l, r, mid) < k) vl = mid + 1;
			else vr = (res = mid) - 1;
		}
		return res;
	}
	
	// Resort the b-th block when adding k to range [l, r].
	inline void msort(int l, int r, int b, int k) {
		t1.clear(), t2.clear();
		for(int i = L[b]; i <= R[b]; i++) {
			if (i >= l && i <= r) a[i] += k;
			if (p[i].pos >= l && p[i].pos <= r) t1.push_back(Node(p[i].val + k, p[i].pos));
			else t2.push_back(p[i]);
		}
		int lp = 0, rp = 0, ptr = L[b];
		while (ptr <= R[b]) {
			if (lp < t1.size() && (rp >= t2.size() || t1[lp].val < t2[rp].val)) p[ptr++] = t1[lp++];
			else p[ptr++] = t2[rp++];
		}
	}
	
	// Add k to range [l, r].
	inline void add(int l, int r, int k) {
		int bl = belong[l], br = belong[r];
		msort(l, r, bl, k);       // Leftmost block
		if (bl != br) {
			msort(l, r, br, k);   // Rightmost block
			for (int i = bl + 1; i < br; i++) tag[i] += k;
		}
	}
};

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	
	const int n = read<int>(), m = read<int>();
	vector<i64> a(n);
	for (int i = 0; i < n; i++) a[i] = read<i64>();
	Block blk(a);
	for (int i = 0, op, l, r, k; i < m; i++) {
		op = read<int>(), l = read<int>(), r = read<int>(), k = read<int>();
		l--, r--;
		if (op == 1) write(blk.kth(l, r, k)), putchar_unlocked('\n');
		else blk.add(l, r, k);
	}
	
	return 0;
}
Logo

集算法之大成!助力oier实现梦想!

更多推荐