hdu 5324 Boring Class(树状数组+笛卡尔树 | 树状数组+cdq分治)

该博客介绍了如何使用树状数组结合笛卡尔树来解决HDU 5324题目的算法。通过从N到1的遍历,树状数组维护第二维度,笛卡尔树处理第三维度,控制树的高度并优化查询效率。同时,文章还提到了从右向左遍历,并按照L值排序ID进行二分查找的方法,用以更新树状数组。
摘要由CSDN通过智能技术生成

题目链接:hdu 5324 Boring Class


做法一:树状数组套笛卡尔树。

保证字典序最小,从N->1方向遍历。树状数组维护第二维,每个节点对应一棵笛卡尔树,笛卡尔树的key值对应第三维,val值为随机值,控制树高。然后笛卡尔树的每个节点维护len,idx,mlen,midx将每次查询复杂度控制在log(n) * log(n)

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 50010;
#define lowbit(x) ((x)&(-x))
struct Node {
	Node* ch[2];
	int key, len, idx, val;
	int mlen, midx;

	Node () {}
	Node (int key, int len, int idx):key(key), len(len), idx(idx), mlen(len), midx(idx) {
		val = rand();
		ch[0] = ch[1] = NULL;
	}

	void maintain() {
		mlen = len;
		midx = idx;

		for (int i = 0; i < 2; i++) {
			if (ch[i] != NULL && (ch[i]->mlen > mlen || (ch[i]->mlen == mlen && ch[i]->midx < midx))) {
				mlen = ch[i]->mlen;
				midx = ch[i]->midx;
			}
		}
	}

	int cmp (int v) {
		if (v == key)
			return -1;
		return v < key ? 0 : 1;
	}
};

void findMax(Node* a, Node* b) {
	if (a->mlen < b->mlen || (a->mlen == b->mlen && a->midx > b->midx))
		*a = *b;
}

namespace Treap {
	int cntNode;
	Node nd[maxn * 10];

	void init () {
		cntNode = 0;
	}

	Node* newNode (int key, int len, int idx) {
		nd[++cntNode] = Node(key, len, idx);
		return &nd[cntNode];
	}

	void rotate(Node* &o, int d) {
		Node* k = o->ch[d^1];
		o->ch[d^1] = k->ch[d];
		k->ch[d] = o;
		o->maintain();
		k->maintain();
		o = k;
	}

	void insert(Node* &o, int key, int len, int idx) {
		if (o == NULL)
			o = newNode(key, len, idx);
		else {
			int d = o->cmp(key);
			if (d != -1) {
				insert(o->ch[d], key, len, idx);
				if (o->val < o->ch[d]->val)
					rotate(o, d^1);
			} else if (len >= o->len) {
				o->len = len;
				o->idx = idx;
			}
		}
		o->maintain();
	}

	Node query(Node* o, int key) {
		if (o == NULL)
			return Node(-1, 0, -1);
		else {
			Node ret, tmp;
			if (o->key == key) {
				ret = Node(o->key, o->len, o->idx);
				if (o->ch[1])
					findMax(&ret, o->ch[1]);
			} else if (o->key > key) {
				ret = Node(o->key, o->len, o->idx);
				if (o->ch[1])
					findMax(&ret, o->ch[1]);

				tmp = query(o->ch[0], key);
				findMax(&ret, &tmp);
			} else {
				ret = query(o->ch[1], key);
			}
			return ret;
		}
	}
}

namespace Bit {

	int n;
	Node* fenw[maxn];

	void init (int k) {
		n = k;
		memset(fenw, 0, sizeof(fenw));
	}

	void add (int x, int key, int len, int idx) {
		while (x < n) {
			Treap::insert(fenw[x], key, len, idx);
			x += lowbit(x);
		}
	}

	Node search(int x, int level) {
		Node ret, tmp;
		ret = Node(-1, 0, -1);
		while (x > 0) {
			tmp = Treap::query(fenw[x], level);
			findMax(&ret, &tmp);
			x -= lowbit(x);
		}
		return ret;
	}
}

struct Pi {
	int L, R, id;
	bool operator < (const Pi& u) const {
		return L < u.L;
	}
}P[maxn];

bool cmp(const Pi& a, const Pi& b) {
	return a.id < b.id;
}
int N, M, Idx[maxn], Pre[maxn];

void print (Node& ans) {
	int mv = ans.midx;
	printf("%d\n", ans.mlen);
	while (true) {
		printf("%d", mv);
		if (Pre[mv] == -1)
			break;
		printf(" ");
		mv = Pre[mv];
	}
	printf("\n");
}

int main () {
	while (scanf("%d", &N) == 1) {
		for (int i = 1; i <= N; i++) {
			scanf("%d", &P[i].L);
			P[i].id = i;
		}
		for (int i = 1; i <= N; i++)
			scanf("%d", &P[i].R);
		sort(P + 1, P + N + 1);

		M = 0;
		Idx[1] = ++M;
		for (int i = 2; i <= N; i++) {
			if (P[i].L != P[i-1].L)
				Idx[i] = ++M;
			else
				Idx[i] = Idx[i-1];
		}
		for (int i = 1; i <= N; i++)
			P[i].L = Idx[i];
		sort(P + 1, P + N + 1, cmp);

		Bit::init(M + 1);
		Treap::init();

		Node ans = Node(-1, 0, -1), tmp;
		for (int i = N; i > 0; i--) {
			tmp = Bit::search(P[i].L, P[i].R);
			Pre[i] = tmp.midx;
			tmp.mlen = tmp.mlen + 1;
			tmp.midx = i;
			Bit::add(P[i].L, P[i].R, tmp.mlen, tmp.midx);
			findMax(&ans, &tmp);
		}
		print(ans);
	}
	return 0;
}


做法二:树状数组+cdq分治。

同样,保证字典序最小,从右向左遍历,即下标为第一维。接着按照L值排序id,每次二分,优先处理后半段,在由后半段更新前半段。树状数组离散R维。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn = 50005;
const int inf = 0x3f3f3f3f;
#define lowbit(x) ((-x)&(x))

struct Pi {
	int idx, L, R;
}P[maxn];

int N, ans, rans, dp[maxn], pre[maxn];
int bw, sy[maxn], ty[maxn << 1], sz[maxn], tz[maxn];

bool cmp (const int& a, const int& b) {
	return P[a].L > P[b].L;
}

namespace Bit {
	int n, fenw[maxn], rec[maxn];

	void init (int k) {
		n = k;
		memset(fenw, 0, (n + 1) * sizeof(int));
	}

	void add (int x, int k, int id) {
		while (x <= n) {
			if (fenw[x] == k)
				rec[x] = min(rec[x], id);
			else if (fenw[x] < k)
				fenw[x] = k, rec[x] = id;
			x += lowbit(x);
		}
	}

	int search (int x, int& id) {
		int ret = id = 0;
		while (x) {
			if (fenw[x] > ret)
				ret = fenw[x], id = rec[x];
			else if (fenw[x] == ret)
				id = min(id, rec[x]);
			x -= lowbit(x);
		}
		return ret;
	}
};

void init () {
	bw = ans = rans = 0;
	for (int i = 1; i <= N; i++)
		scanf("%d", &P[i].L);
	for (int i = 1; i <= N; i++) {
		scanf("%d", &P[i].R);
		sy[i] = i, dp[i] = 1;
	}
	sort(sy + 1, sy + N + 1, cmp);
}

void solve (int l, int r) {
	if (l == r) {
		if (dp[l] > ans)
			ans = dp[l], rans = l;
		else if (dp[l] == ans)
			rans = min(rans, l);
		sz[l] = P[l].R;
		return;
	}

	int mid = (l + r) >> 1, len = r - l + 1;
	int ml = l, mr = mid + 1;
	memcpy(ty + bw, sy + l, len * sizeof(int));
	for (int i = 0; i < len; i++) {
		if (ty[bw+i] <= mid)
			sy[ml++] = ty[bw+i];
		else
			sy[mr++] = ty[bw+i];
	}
	bw += len;
	solve(mid + 1, r);
	int n = r - mid, limt = bw, pos, ri = mid + 1;
	bw -= len;

	Bit::init(n);
	//printf("%d %d %d:\n", l, r, len);
	for (int i = limt - 1; i >= bw; i--) {
		if (ty[i] > mid) {
			pos = lower_bound(sz + ri, sz + ri + n, P[ty[i]].R) - (sz + ri);
			Bit::add(n - pos, dp[ty[i]], ty[i]);
			//printf("add: %d %d %d\n", n - pos, dp[i], i);
			continue;
		}

		pos = lower_bound(sz + ri, sz + ri + n, P[ty[i]].R) - (sz + ri);
		
		//printf("check:%d\n", pos);

		if (pos >= 0) {
			int tmp;
			int ret = Bit::search(n - pos, tmp) + 1;
			//printf("search:%d %d %d\n", n - pos, ret, tmp);

			if (dp[ty[i]] < ret)
				dp[ty[i]] = ret, pre[ty[i]] = tmp;
			else if (dp[ty[i]] == ret)
				pre[ty[i]] = min(pre[ty[i]], tmp);
		}
	}

	solve(l, mid);
	merge(sz + l, sz + ri, sz + ri, sz + l + len, tz);
	memcpy(sz + l, tz, len * sizeof(int));
}

int main () {
	while (scanf("%d", &N) == 1) {
		init();
		solve(1, N);
		printf("%d\n", ans);
		for (int i = 1; i <= ans; i++) {
			printf("%d%c", rans, i == ans ? '\n' : ' ');
			rans = pre[rans];
		}
	}
	return 0;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值