[ZJOI2013]K大数查询

权值线段树套线段树模板
区间每个可重集插入一个数
把权值放外边,内部维护区间
在权值线段树上二分,内部查询数量

( ext{Code})

#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;

const int N = 5e4;
int n, m, rt, seg_size, tr_size;
struct segment{int ls, rs, tg; LL s;}seg[N * 270 + 5];
struct tree{int ls, rs, rt;}T[N * 8 + 5];

inline void pushdown(int p, int tl, int tr)
{
	if (!seg[p].tg)	return;
	LL v = seg[p].tg;
	if (!seg[p].ls) seg[p].ls = ++seg_size; 
	if (!seg[p].rs) seg[p].rs = ++seg_size;
	int mid = (tl + tr) >> 1;
	seg[seg[p].ls].s += v * (mid - tl + 1), seg[seg[p].ls].tg += v;
	seg[seg[p].rs].s += v * (tr - mid), seg[seg[p].rs].tg += v;
	seg[p].tg = 0;
}
void update(int &p, int tl, int tr, int l, int r)
{
	if (!p) p = ++seg_size;
	seg[p].s += min(tr, r) - max(tl, l) + 1;
	if (l <= tl && tr <= r) return void(++seg[p].tg);
	pushdown(p, tl, tr);
	int mid = (tl + tr) >> 1;
	if (l <= mid) update(seg[p].ls, tl, mid, l, r);
	if (r > mid) update(seg[p].rs, mid + 1, tr, l, r);
}
LL query(int p, int tl, int tr, int l, int r)
{
	if (!p || (l <= tl && tr <= r)) return seg[p].s;
	pushdown(p, tl, tr);
	int mid = (tl + tr) >> 1; LL res = 0;
	if (l <= mid) res = query(seg[p].ls, tl, mid, l, r);
	if (r > mid) res += query(seg[p].rs, mid + 1, tr, l, r);
	return res;
}

void Update(int &p, int tl, int tr, int l, int r, LL k)
{
	if (!p) p = ++tr_size;
	update(T[p].rt, 1, n, l, r);
	if (tl == tr) return;
	int mid = (tl + tr) >> 1;
	if (k <= mid) Update(T[p].ls, tl, mid, l, r, k);
	else Update(T[p].rs, mid + 1, tr, l, r, k);
}
int Query(int p, int tl, int tr, int l, int r, LL k)
{
	if (tl == tr) return tl;
	int mid = (tl + tr) >> 1;
	LL sum = query(T[T[p].rs].rt, 1, n, l, r);
	if (k <= sum) return Query(T[p].rs, mid + 1, tr, l, r, k);
	return Query(T[p].ls, tl, mid, l, r, k - sum);
}

int main()
{
	scanf("%d%d", &n, &m);
	int l, r, op; LL k;
	for(; m; --m)
	{
		scanf("%d%d%d%lld", &op, &l, &r, &k);
		if (op == 1) Update(rt, -n, n, l, r, k);
		else printf("%d
", Query(rt, -n, n, l, r, k));
	}
}

整体二分的话就很套路了
维护用的数据结构改成区间加操作的线段树即可
注意划分时的答案范围

(Code)

#include <cstdio>
#include <iostream>
#define ls (p << 1)
#define rs (ls | 1)
#define re register
#define LL long long
using namespace std;

const int N = 5e4 + 5;
int n, m, tot, ans[N];
struct node{int op, l, r, id; LL k;}Q[N], q1[N], q2[N];

LL seg[N * 4], tag[N * 4];
inline void pushdown(int p, int l, int r)
{
	if (!tag[p]) return;
	int mid = (l + r) >> 1;
	seg[ls] += tag[p] * (mid - l + 1), tag[ls] += tag[p];
	seg[rs] += tag[p] * (r - mid), tag[rs] += tag[p];
	tag[p] = 0;
}
void update(int p, int l, int r, int tl, int tr, int v)
{
	seg[p] += (min(r, tr) - max(l, tl) + 1) * v;
	if (tl <= l && r <= tr) return void(tag[p] += v);
	pushdown(p, l, r);
	int mid = (l + r) >> 1;
	if (tl <= mid) update(ls, l, mid, tl, tr, v);
	if (tr > mid) update(rs, mid + 1, r, tl, tr, v);
}
LL query(int p, int l, int r, int tl, int tr)
{
	if (tl <= l && r <= tr) return seg[p];
	pushdown(p, l, r);
	int mid = (l + r) >> 1; LL res = 0;
	if (tl <= mid) res = query(ls, l, mid, tl, tr);
	if (tr > mid) res += query(rs, mid + 1, r, tl, tr);
	return res;
}

void solve(int l, int r, int ql, int qr)
{
	if (ql > qr) return;
	if (l == r)
	{
		for(re int i = ql; i <= qr; i++)
		if (Q[i].op == 2) ans[Q[i].id] = l;
		return;
	}
	int ct1 = 0, ct2 = 0, mid = (l + r) >> 1;
	for(re int i = ql; i <= qr; i++)
	{
		if (Q[i].op == 1) 
		{
			if (Q[i].k > mid) update(1, 1, n, Q[i].l, Q[i].r, 1), q1[++ct1] = Q[i];
			else q2[++ct2] = Q[i];
		}
		else{
			LL sum = query(1, 1, n, Q[i].l, Q[i].r);
			if (Q[i].k <= sum) q1[++ct1] = Q[i];
			else Q[i].k -= sum, q2[++ct2] = Q[i];
		}
	}
	for(re int i = 1; i <= ct1; i++)
	if (q1[i].op == 1) update(1, 1, n, q1[i].l, q1[i].r, -1);
	for(re int i = 1; i <= ct1; i++) Q[ql + i - 1] = q1[i];
	for(re int i = 1; i <= ct2; i++) Q[ql + ct1 - 1 + i] = q2[i];
	solve(mid + 1, r, ql, ql + ct1 - 1), solve(l, mid, ql + ct1, qr);
}

int main()
{
	scanf("%d%d", &n, &m);
	int op, l, r; LL k;
	for(re int i = 1; i <= m; i++)
	{
		scanf("%d%d%d%lld", &Q[i].op, &Q[i].l, &Q[i].r, &Q[i].k);
		if (Q[i].op == 2) Q[i].id = ++tot;
	}
	solve(-n, n, 1, m);
	for(re int i = 1; i <= tot; i++) printf("%d
", ans[i]);
}

发现树套树好像更好打(又短)
但整体二分跑得是真的快

原文地址:https://www.cnblogs.com/leiyuanze/p/15369477.html