[BZOJ3658]Jabberwocky

试题描述

平面上有n个点,每个点有k种颜色中的一个。
你可以选择一条水平的线段获得在其上方或其下方的所有点,如图所示:

请求出你最多能够得到多少点,使得获得的点并不包含所有的颜色。

输入

包含多组测试数据,第一行输入一个数T表示测试数据组数。
接下来T组测试数据,对于每组测试数据,第一行输入两个数n,k,分别表示点的个数和颜色数。
接下来n行每行描述一个点,前两个数z,y(lxl,lyl≤2^32-1)描述点的位置,最后一个数z(1≤z≤K)描述点的颜色。

输出

对于每组数据输出一行,每行一个数ans,表示答案。

输入示例

1
10 3
1 2 3
2 1 1
2 4 2
3 5 3
4 4 2
5 1 2
6 3 1
6 7 1
7 2 3
9 4 2

输出示例

5

数据规模及约定

N<=100000,K<=100000,T<=3

题解

题目要求不包含所有颜色,即至少有一个颜色不被包含,我们可以枚举这个不被包含的颜色。

先考虑某条线段下的情况,假设当前枚举的不包含的颜色为 x,接下来想象一条扫描线往上走,遇到颜色 x 的点就得被这个点劈成两半(同时在这个时刻统计并更新答案),接下来这两半分别进行同样的操作。

对于某条线段上的情况,做法是对称的。

我们可以用分治的方法模拟这个过程,因为上面的描述显然符合子问题和当前问题一模一样的条件,需要做的就是用数据结构优化这些操作:

1.) 找到最先碰到的颜色为 x 的点,可以用线段树,按照 x 坐标存点,维护每个区间内点的 y 坐标的最大、最小值。

2.) 求一个矩形内部点的个数,可以用主席树,每个 y 坐标作为一个版本,按 y 从小到大维护 x 坐标的个数。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
	if(Head == Tail) {
		int l = fread(buffer, 1, BufferSize, stdin);
		Tail = (Head = buffer) + l;
	}
	return *Head++;
}
int read() {
	int x = 0, f = 1; char c = Getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
	return x * f;
}

#define maxn 100010
#define maxnode 6666666
#define oo 2147483647

int n, num[maxn<<1], cntn, head[maxn], next[maxn];
struct Point {
	int x, y;
	Point() {}
	Point(int _, int __): x(_), y(__) {}
} ps[maxn], p2[maxn];
bool cmpx(Point a, Point b) { return a.x < b.x; }
bool cmpy(Point a, Point b) { return a.y < b.y; }

int ToT, rt[maxn<<1], sumv[maxnode], lc[maxnode], rc[maxnode];
void update(int& y, int x, int l, int r, int p) {
	sumv[y = ++ToT] = sumv[x] + 1;
	if(l == r) return ;
	int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
	if(p <= mid) update(lc[y], lc[x], l, mid, p);
	else update(rc[y], rc[x], mid + 1, r, p);
	return ;
}
void build() {
	ToT = 0;
	memset(sumv, 0, sizeof(sumv));
	memset(lc, 0, sizeof(lc));
	memset(rc, 0, sizeof(rc));
	sort(p2 + 1, p2 + n + 1, cmpy);
	for(int y = 1, i = 1; y <= cntn; y++) {
		rt[y] = rt[y-1];
		while(i <= n && p2[i].y == y) update(rt[y], rt[y], 1, cntn, p2[i].x), i++;
	}
	return ;
}
int query(int o, int l, int r, int ql, int qr) {
	if(!o) return 0;
	if(ql <= l && r <= qr) return sumv[o];
	int mid = l + r >> 1, ans = 0;
	if(ql <= mid) ans += query(lc[o], l, mid, ql, qr);
	if(qr > mid) ans += query(rc[o], mid + 1, r, ql, qr);
	return ans;
}

Point mxp[maxn<<3], mnp[maxn<<3];
void init_seg(int L, int R, int o) {
	if(L == R) mxp[o] = Point(L, -233), mnp[o] = Point(R, oo);
	else {
		int M = L + R >> 1, lc = o << 1, rc = lc | 1;
		init_seg(L, M, lc); init_seg(M+1, R, rc);
		mxp[o] = Point(L, -233); mnp[o] = Point(R, oo);
	}
	return ;
}
void modify(int L, int R, int o, Point p) {
	if(L == R) {
		if(p.y > mxp[o].y) mxp[o] = p;
		if(p.y < mnp[o].y) mnp[o] = p;
	}
	else {
		int M = L + R >> 1, lc = o << 1, rc = lc | 1;
		if(p.x <= M) modify(L, M, lc, p);
		else modify(M+1, R, rc, p);
		if(mxp[lc].y > mxp[rc].y) mxp[o] = mxp[lc]; else mxp[o] = mxp[rc];
		if(mnp[lc].y < mnp[rc].y) mnp[o] = mnp[lc]; else mnp[o] = mnp[rc];
	}
	return ;
}
void clear(int L, int R, int o, Point p) {
	if(L == R) mxp[o] = Point(L, -233), mnp[o] = Point(R, oo);
	else {
		int M = L + R >> 1, lc = o << 1, rc = lc | 1;
		if(p.x <= M) clear(L, M, lc, p);
		else clear(M+1, R, rc, p);
		mxp[o] = Point(L, -233); mnp[o] = Point(R, oo);
	}
	return ;
}
Point querymx(int L, int R, int o, int ql, int qr) {
	if(ql <= L && R <= qr) return mxp[o];
	int M = L + R >> 1, lc = o << 1, rc = lc | 1;
	Point res(L, -233), tmp;
	if(ql <= M) {
		tmp = querymx(L, M, lc, ql, qr);
		if(res.y < tmp.y) res = tmp;
	}
	if(qr > M) {
		tmp = querymx(M+1, R, rc, ql, qr);
		if(res.y < tmp.y) res = tmp;
	}
	return res;
}
Point querymn(int L, int R, int o, int ql, int qr) {
	if(ql <= L && R <= qr) return mnp[o];
	int M = L + R >> 1, lc = o << 1, rc = lc | 1;
	Point res(R, oo), tmp;
	if(ql <= M) {
		tmp = querymn(L, M, lc, ql, qr);
		if(res.y > tmp.y) res = tmp;
	}
	if(qr > M) {
		tmp = querymn(M+1, R, rc, ql, qr);
		if(res.y > tmp.y) res = tmp;
	}
	return res;
}
void build_seg(int col) {
	for(int i = head[col]; i; i = next[i]) modify(1, cntn, 1, ps[i]);
	return ;
}
void remove_seg(int col) {
	for(int i = head[col]; i; i = next[i]) clear(1, cntn, 1, ps[i]);
	return ;
}
int ans;
void Solve_down(int l, int r) {
	if(l > r) return ;
	Point tmp = querymn(1, cntn, 1, l, r);
	// matrix: x[l, r], y[1, tmp.y - 1]
	if(tmp.y < oo) ans = max(ans, query(rt[tmp.y-1], 1, cntn, l, r));
	else {
		ans = max(ans, query(rt[cntn], 1, cntn, l, r));
		return ;
	}
	Solve_down(l, tmp.x - 1); Solve_down(tmp.x + 1, r);
	return ;
}
void solve_down(int col) {
	build_seg(col);
	Solve_down(1, cntn);
	remove_seg(col);
	return ;
}
void Solve_up(int l, int r) {
	if(l > r) return ;
	Point tmp = querymx(1, cntn, 1, l, r);
	// matrix: x[l, r], y[tmp.y + 1, cntn]
//	printf("query_up: %d %d\n", tmp.x, tmp.y);
	if(tmp.y >= 0) ans = max(ans, query(rt[cntn], 1, cntn, l, r) - query(rt[tmp.y], 1, cntn, l, r));
	else {
		ans = max(ans, query(rt[cntn], 1, cntn, l, r));
		return ;
	}
//	printf("ans: %d\n", ans);
	Solve_up(l, tmp.x - 1); Solve_up(tmp.x + 1, r);
	return ;
}
void solve_up(int col) {
	build_seg(col);
	Solve_up(1, cntn);
	remove_seg(col);
	return ;
}

int main() {
	int T = read();
	while(T--) {
		memset(head, 0, sizeof(head));
		n = read(); int col = read(); cntn = 0;
		for(int i = 1; i <= n; i++) {
			int x = read(), y = read(), c = read();
			num[++cntn] = x; num[++cntn] = y;
			ps[i] = Point(x, y); next[i] = head[c]; head[c] = i;
		}
		sort(num + 1, num + cntn + 1);
		cntn = unique(num + 1, num + cntn + 1) - num - 1;
		for(int i = 1; i <= n; i++)
			ps[i].x = lower_bound(num + 1, num + cntn + 1, ps[i].x) - num,
			ps[i].y = lower_bound(num + 1, num + cntn + 1, ps[i].y) - num,
			p2[i] = ps[i];
		/*for(int i = 1; i <= n; i++) printf("%d %d\n", ps[i].x, ps[i].y);
		for(int i = 1; i <= col; i++) {
			printf("%d:", i);
			for(int j = head[i]; j; j = next[j]) printf(" (%d, %d)", ps[j].x, ps[j].y);
			putchar(‘\n‘);
		}*/
		build();
		ans = 0;
		init_seg(1, cntn, 1);
//		puts("here");
		for(int i = 1; i <= col; i++) solve_down(i);
		for(int i = 1; i <= col; i++) solve_up(i);
		printf("%d\n", ans);
	}

	return 0;
}
时间: 2024-12-14 23:51:07

[BZOJ3658]Jabberwocky的相关文章

【BZOJ-4548&amp;3658】小奇的糖果&amp;Jabberwocky 双向链表 + 树状数组

4548: 小奇的糖果 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 103  Solved: 47[Submit][Status][Discuss] Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾 起多少糖果,使得获得的糖果并不包含所有的颜色. Input 包含多组测试数据,第一行输入一个正整数 T 表示测试数据组数. 接下来 T 组测试数据,对于每组测试数据,第

BZOJ 3658 Jabberwocky 可持久化线段树+分治

题目大意:给定平面上n个点,一共有k种颜色,要求选定一条线段,并选取线段正上方或正下方的所有点,要求不能出现所有颜色的点,求最多选择多少点 正解是双向链表+树状数组? 让我们来点优雅的做法 由于不能出现所有颜色的点 因此一定有至少一种颜色不出现 我们可以枚举这个不出现的颜色 现在我们搞出所有极大子矩形 这个分治就好了... 假设我们现在求的是一条线段下方的点 那么我们考虑一条直线从下往上走 遇到禁止的点就分成两段 继续 那么我们利用分治来模拟这个过程 定义Solve(l,r)为处理横坐标在[l,

[乱搞 树状数组] BZOJ 4548 小奇的糖果 &amp;&amp; BZOJ 3658 Jabberwocky

跟悬线法有点像 #include<cstdio> #include<cstdlib> #include<algorithm> #include<cstring> #define cl(x) memset(x,0,sizeof(x)) using namespace std; inline char nc() { static char buf[100000],*p1=buf,*p2=buf; if (p1==p2) { p2=(p1=buf)+fread(b

JAVA 正则表达式 (超详细)

(PS:这篇文章为转载,我不喜欢转载的但我觉得这篇文章实在是超赞了,就转了过来,这篇可以说是学习JAVA正则表达的必读篇.作者是个正真有功力的人,阅读愉快) 在Sun的JavaJDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu x用户都会遇到正则表达式.正则表达式是个极端强大工具,而且在字符串模式-匹配和字符串模式-替换方面富有弹性.在Unix世界里,正则表达式几乎没有

Java正则表达式实例详解

创建正则表达式 你可以从比较简单的东西入手学习正则表达式.要想全面地掌握怎样构建正则表达式,可以去看JDK 文档的java.util.regex 的Pattern 类的文档. 字符 B 字符B \xhh 16进制值0xhh 所表示的字符 \uhhhh 16进制值0xhhhh 所表示的Unicode字符 \t Tab \n 换行符 \r 回车符 \f 换页符 \e Escape 正则表达式的强大体现在它能定义字符集(character class).下面是一些最常见的字符集及其定义的方式,此外还有

Python 上下文管理器和else块

最终,上下文管理器可能几乎与子程序(subroutine)本身一样重要.目前,我们只了解了上下文管理器的皮毛--Basic 语言有with 语句,而且很多语言都有.但是,在各种语言中 with 语句的作用不同,而且做的都是简单的事,虽然可以避免不断使用点号查找属性,但是不会做事前准备和事后清理.不要觉得名字一样,就意味着作用也一样.with 语句是非常了不起的特性.  --Raymond Hettinger 雄辩的 Python 布道者 先做这个,再做那个:if语句之外的else块 这个语言特性

Java正则表达式详解

本文为转载文章,因为实在写的太好了,所以就拿过来使用了 在Sun的Java JDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu x用户都会遇到正则表达式.正则表达式是个极端强大工具,而且在字符串模式-匹配和字符串模式-替换方面富有弹性.在Unix世界里,正则表达式几乎没有什么限制,可肯定的是,它应用非常之广泛. 正则表达式的引擎已被许多普通的Unix工具所实现,包括gr

转 正则表达式超详细讲解

注**该文中的/写反的地方实践中自行改正!!! (PS:这篇文章为转载,我不喜欢转载的但我觉得这篇文章实在是超赞了,就转了过来,这篇可以说是学习JAVA正则表达的必读篇.作者是个正真有功力的人,阅读愉快) 在Sun的Java JDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu x用户都会遇到正则表达式.正则表达式是个极端强大工具,而且在字符串模式-匹配和字符串模式-替换

【转】java正则表达式

在Sun的Java JDK 1.40版本中,Java自带了支持正则表达式的包,本文就抛砖引玉地介绍了如何使用java.util.regex包. 可粗略估计一下,除了偶尔用Linux的外,其他Linu x用户都会遇到正则表达式.正则表达式是个极端强大工具,而且在字符串模式-匹配和字符串模式-替换方面富有弹性.在Unix世界里,正则表达式几乎没有什么限制,可肯定的是,它应用非常之广泛. 正则表达式的引擎已被许多普通的Unix工具所实现,包括grep,awk,vi和Emacs等.此外,许多使用比较广泛