贴一个划分树套主席树

题目大意:给一个每行每列有且只有一个元素的矩阵,每次询问(l, r, a, b, k),意义为第l到r行中元素的位置排名第a到b中元素的第k大值。

一开始yy了一个分块,由于询问有50w级别,被卡到30s才出解。。。

此题要在区间ath,bth上再维护一个k大值,因此目测只有O(logn)的区间kth才可以过。。。

众所周知,静态区间kth能达到O(logn)per operation的有主席树和划分树。而区间kth满足区间加法,却不满足区间减法,因此暂且不考虑依赖区间减法的主席树。

虽然划分树也是以区间减法完成对区间kth的操作的,但是划分树有一个很好的性质:在树的每一个节点,第a到b大值是连续的。这意味着对于全局的(a, b), 在树的每一个节点中在区间(a, b)中的元素也是连续的。并且可以发现对于一个名次区间(a, b),在划分树中对应的区间是O(logn)的(类比线段树)。这为这道题提供了解法。

对于划分树的每一个节点建立一颗主席树(也可以是划分树,不过更为复杂),对于每一个询问(l, r, a, b, k),利用划分树找到对应的O(logn)颗主席树,在将这些主席树并起来查询。这样就可以在较优复杂度内解决问题,即O(logn + log2n) = O(log2n) per operation。

对于前50%的数据(k = 1, |query| <= 500000),可以考虑划分树套O(1)查询的rmq,这样可以做到O(logn) per operation。

至此此题很好地解决了。

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cctype>
#include <map>
#include <set>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

#ifdef WIN32
#define fmt64 "%I64d"
#else
#define fmt64 "%lld"
#endif
#define PI M_PI
#define oo 0x13131313
#define PB push_back
#define PO pop_back
#define iter iterator
#define MP make_pair
#define fst first
#define snd second
#define cstr(a) (a).c_str()

#define FOR(i, j, k) for (i = (j); i <= (k); ++i)
#define ROF(i, j, k) for (i = (j); i >= (k); --i)
#define FER(i, j, k) for (i = j[k]; i; i = i->n)
#define FRE(i, a) for (i = (a).begin(); i != (a).end(); ++i)

typedef unsigned int uint;
typedef long long int64;
typedef unsigned long long uint64;
typedef long double real;

template<class T> inline bool minim(T &a, const T &b) {return b < a ? a = b, 1 : 0;}
template<class T> inline bool maxim(T &a, const T &b) {return b > a ? a = b, 1 : 0;}
template<class T> inline T sqr(const T &a) {return a * a;}

template<class T> inline void read(T &x)
{
	char c = getchar(); bool f = 0;
	for (; '0' > c || c > '9'; c = getchar()) if (c == '-') f = 1;
	x = c - '0', c = getchar();
	for (; '0' <= c && c <= '9'; c = getchar()) (x *= 10) += c - '0';
	if (f) x = -x;
}

#define maxn 30005
#define maxv 16

bool data_type;
int n, Q, E, ans;
int a[maxn], b[maxn], c[maxn];

struct segt {segt *s, *t; int l, r, v;};
segt ns[maxn * maxv * maxv * 2], *nt = ns;

void build_segt(segt *&p, int l, int r)
{
	p = ++nt, p->l = l, p->r = r;
	if (l == r) return;
	build_segt(p->s, l, (l + r) >> 1);
	build_segt(p->t, ((l + r) >> 1) + 1, r);
}

void update(segt *&p, segt *f, int pos)
{
	p = ++nt, *p = *f, ++p->v;
	if (p->l == p->r) return;
	pos < p->t->l ? update(p->s, f->s, pos) : update(p->t, f->t, pos);
}

struct part
{
	int v[maxn], s[maxn];
	segt *root[maxn];
	int st[maxv][maxn];
};

part ps[maxv], *maxdepth;

void build_part(part *p, int l, int r)
{
	if (maxim(maxdepth, p), l == r) return;
	int *a = (p + 1)->v, i;
	int mid = (l + r) >> 1, x = l - 1, y = mid;

	FOR(i, l, r) if (p->v[i] > mid)
		a[++y] = p->v[i], p->s[i] = p->s[i - 1];
	else
		a[++x] = p->v[i], p->s[i] = p->s[i - 1] + 1;

	build_part(p + 1, l, mid);
	build_part(p + 1, mid + 1, r);
}

int LOG2[maxn], mini;

void init_st()
{
	int i, j = 2;
	FOR(i, 1, n) if (LOG2[i] = LOG2[i - 1], i == j) ++LOG2[i], j <<= 1;
	for (part *p = ps; p <= maxdepth; ++p) {
		int *st = p->st[0], *v = p->v;
		FOR(i, 1, n) st[i] = c[v[i]];
		FOR(i, 1, LOG2[n]) {
			int *q = st; st = p->st[i];
			FOR(j, 1, n) st[j] = min(q[j], q[j + (1 << (i - 1))]);
		}
	}
}

int get_min(part *p, int l, int r)
{
	if (l == r) return c[p->v[l]];
	int k = LOG2[r - l + 1];
	return min(p->st[k][l], p->st[k][r - (1 << k) + 1]);
}

void init_segt()
{
	int i;
	for (part *p = ps; p <= maxdepth; ++p) {
		segt **root = p->root; int *v = p->v;
		build_segt(root[0], 1, n);
		FOR(i, 1, n) update(root[i], root[i - 1], c[v[i]]);
	}
}

struct info {segt *v; char c;} stk[maxv * 4], *top;

void get_info(part *p, int l, int r, int x, int y, int a, int b)
{
	if (a == 1 && b == y - x + 1) {
		minim(mini, get_min(p, x, y));
		*(++top) = (info){p->root[x - 1], -1};
		*(++top) = (info){p->root[y], 1};
		return;
	}

	int mid = (l + r) >> 1;
	int u = p->s[x - 1] - p->s[l - 1];
	int v = p->s[y] - p->s[l - 1];

	if (v - u >= b)
		return get_info(p + 1, l, mid, l + u, l + v - 1, a, b);
	if (v - u < a)
		return get_info(p + 1, mid + 1, r, mid + 1 + x - l - u, mid + 1 + y - l - v, a - (v - u), b - (v - u));

	get_info(p + 1, l, mid, l + u, l + v - 1, a, v - u);
	get_info(p + 1, mid + 1, r, mid + 1 + x - l - u, mid + 1 + y - l - v, 1, b - (v - u));
}

int get_kth(int k)
{
	int res = 0;
	for (int l = 1, r = n; l < r; res = l) {
		int mid = (l + r) >> 1, rank = 0;

		for (info *i = top; i > stk; --i) rank += i->v->s->v * i->c;
		if (k > rank) {
			for (info *i = top; i > stk; --i) i->v = i->v->t;
			l = mid + 1, k -= rank;
		} else {
			for (info *i = top; i > stk; --i) i->v = i->v->s;
			r = mid;
		}
	}
	return res;
}

int query(int l, int r, int a, int b, int k)
{
	top = stk, mini = oo;
	get_info(ps, 1, n, l, r, a, b);
	return data_type ? get_kth(k) : mini;
}

void init()
{
	int i; read(n);
	FOR(i, 1, n) read(a[i]);
	FOR(i, 1, n) read(b[i]), c[a[i]] = b[i];
	FOR(i, 1, n) ps->v[i] = a[i];
	build_part(ps, 1, n);
}

int main()
{
	freopen("eureka.in", "r", stdin);
	freopen("eureka.out", "w", stdout);

	data_type = getchar() >= 'E';
	init();
	if (data_type) init_segt();
	else init_st();

	for (read(Q), read(E); Q--; ) {
		int l, r, a, b, k;
		read(l), read(r), read(a), read(b), read(k);
		k = (k - 1 + E * ans) % (b - a + 1) + 1;
		ans = query(l, r, a, b, k);
		printf("%d\n", ans);
	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值