当前位置: 首页 > news >正文

P5356 [Ynoi Easy Round 2017] 由乃打扑克 Solution

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 的数的个数,具体就是散块暴力,整块二分,外层二分出的结果就是答案.

再考虑 add ⁡ \operatorname{add} add,整块显然打上 tag \textit{tag} tag
对于散块,由于 a i a_i ai p i p_i pi 都要更新,我们将加过 x x x 和没加过 x x x 的部分分成两个数组,再进行归并.

注意分块的标记是永久化的,计算时不要漏了.
要开 long long,否则会被 hack.

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)

#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;
}

#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;
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);
		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);
		}
	}
	
	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};
	}
	
	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;
	}
	
	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;
	}
	
	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++];
		}
	}
	
	inline void add(int l, int r, int k) {
		int bl = belong[l], br = belong[r];
		msort(l, r, bl, k);
		if (bl != br) {
			msort(l, r, br, k);
			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;
}

相关文章:

  • Redis分布式寻址算法
  • XXL-Job 二次分片是怎么做的?有什么问题?怎么去优化的?
  • ARCGIS PRO SDK ProWindow自定义窗口DataGrid控件的应用
  • langchain-ollama的ragflow简单实现
  • 车载以太网网络测试 -23【TCPUDP通信示例】
  • 模糊规则激活方法详解(python实例对比)
  • 【Tauri2】001——安装及运行
  • shadcn如何给dialog增加关闭按钮和隐藏右上角关闭按钮
  • 重写ring3 API函数
  • 安宝特应用 | 军工级数据安全赋能保密产品数字化交付
  • 第五章 动态规划
  • Linux文件描述符及重定向
  • 26考研——图_图的存储(6)
  • Python学习笔记(6)
  • 在计算进程D状态持续时间及等IO的时间遇到的一处问题
  • Resource usage
  • Flink 流处理框架的核心特性
  • PostgreSQL 连接数超限问题
  • 流程控制语句
  • 每日总结3.24
  • 马上评|让“贾宝玉是长子长孙”争议回归理性讨论
  • 印度杰纳布河上游两座水电站均已重新开闸
  • 美联储如期按兵不动,强调“失业率和通胀上升的风险均已上升”(声明全文)
  • 全国首例在沪完成,这项近视治疗手术不到10秒
  • 姜再冬大使会见巴基斯坦副总理兼外长达尔
  • 国务院安委会办公室印发通知:坚决防范遏制重特大事故发生