HDU - 1199 Color the Ball (动态线段树 // 离散化)

因为昨天做了一道动态线段树的缘故,今天遇到了这题没有限制范围的题就自然而然想到了动态线段树的解法,写完看题解发现原来只要离散化就好了(干。。),总结了一下这题和昨天hdu5367的区别在于,虽然都是两题范围超级大的线段树,但是昨天的强制要求在线求解,只能选择空间复杂度更大一些的动态线段树来求解,而今天的这题可以选择离线操作,因而可以采用先读入所有输入,离散化之后建树的方法来操作。下次遇到这样的题还是应当优先考虑离散化。

题意

There are infinite balls in a line (numbered 1 2 3 ....), and initially all of them are paint black. Now Jim use a brush paint the balls, every time give two integers a b and follow by a char 'w' or 'b', 'w' denotes the ball from a to b are painted white, 'b' denotes that be painted black. You are ask to find the longest white ball sequence. 

Input

First line is an integer N (<=2000), the times Jim paint, next N line contain a b c, c can be 'w' and 'b'. 

There are multiple cases, process to the end of file. 

Output

Two integers the left end of the longest white ball sequence and the right end of longest white ball sequence (If more than one output the small number one). All the input are less than 2^31-1. If no such sequence exists, output "Oh, my god". 

Sample Input

3
1 4 w
8 11 w
3 5 b

Sample Output

8 11

在一个全部涂黑色的条子上涂上一些白色或黑色的片段,问最大白色片段。

仅仅从线段树维护节点的角度上来看很简单,维护最大白色片段,左边最大白色片段,右边最大白色片段就好了。

由于条子的长度长达1-INT_MAX;

采用离散化,或者像我一样失了智去用动态线段树的方法,也能AC

附动态线段树AC代码

#include <map>
#include <set>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
#define For(i, x, y) for(int i=x; i<=y; i++)  
#define _For(i, x, y) for(int i=x; i>=y; i--)
#define Mem(f, x) memset(f, x, sizeof(f))  
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);  
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);  
#define CLR(u) for(int i = 0; i <= N ; i ++) u[i].clear();
#define LL long long
#define ULL unsigned long long  
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second 
using namespace std;
typedef vector<int> VI;
const double eps = 1e-9;
const int maxn = 2010;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7; 
inline int read()
{
	int now=0;register char c=getchar();
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);now=now*10+c-'0',c=getchar());
	return now;
}
int N,M;
struct Tree{
	LL sum;    //最大连续 
	LL lsum;   //左连续 
	LL rsum;   //右连续 
	int lt;
	int rt;
	int lazy;
	void init(){
		lsum = rsum = sum = lt = rt = 0;
		lazy = -1;
	}
}tree[maxn * 60];
int tot;
void check(int &t){
	if(t) return;
	t = ++tot;
	tree[t].init();
}
void add(int &t,LL L,LL R,int v){
	if(v){
		tree[t].sum = tree[t].lsum = tree[t].rsum = R - L + 1;
	}else{
		tree[t].sum = tree[t].lsum = tree[t].rsum = 0;
	}
	tree[t].lazy = v;
}
void Pushdown(int& t,LL L,LL R){
	if(tree[t].lazy == -1) return;
	int &lt = tree[t].lt; int &rt = tree[t].rt;
	LL M = (L + R) >> 1;
	check(lt); check(rt);
	add(lt,L,M,tree[t].lazy);
	add(rt,M + 1,R,tree[t].lazy);
	tree[t].lazy = -1;
}
void Pushup(int t,LL L,LL R){
	int &ls = tree[t].lt; int &rs = tree[t].rt;
	LL M = (L + R) >> 1; 
	check(ls); check(rs);
	tree[t].sum = max(tree[ls].sum,tree[rs].sum);
	tree[t].sum = max(tree[t].sum,tree[ls].rsum + tree[rs].lsum);
	tree[t].lsum = tree[ls].lsum;
	if(tree[ls].lsum == M - L + 1){
		tree[t].lsum = tree[ls].lsum + tree[rs].lsum;
	} 
	tree[t].rsum = tree[rs].rsum;
	if(tree[rs].rsum == R - M){
		tree[t].rsum = tree[rs].rsum + tree[ls].rsum;
	}
}
void update(int &t,int q1,int q2,LL L,LL R,int v){
	check(t);
	if(q1 <= L && R <= q2){
		add(t,L,R,v);
		return;
	}
	Pushdown(t,L,R);
	LL m = (L + R) >> 1;
	if(q1 <= m) update(tree[t].lt,q1,q2,L,m,v);
	if(q2 > m) update(tree[t].rt,q1,q2,m + 1,R,v);
	Pushup(t,L,R);
}
LL Left,Right;
void query(int t,LL L,LL R){
	if(L == R){
		Left = L;
		Right = R;
		return;
	}
	check(tree[t].lt); check(tree[t].rt);
	int ls = tree[t].lt; int rs = tree[t].rt;
	LL M = (L + R) >> 1;
	if(tree[ls].sum == tree[t].sum) query(ls,L,M);
	else if(tree[rs].sum == tree[t].sum) query(rs,M + 1,R);
	else{
		Left = M - tree[ls].rsum + 1;
		Right = M + tree[rs].lsum;
		return;
	}

}
int main()
{
	while(~Sca(N)){
		LL L = 1; LL R = 2147483647;
		tot = 0;
		int root = 0;
		update(root,L,R,L,R,0);
		For(i,1,N){
			LL l,r;
			char op[3];
			scanf("%lld%lld%s",&l,&r,&op);
			if(op[0] == 'w'){
				update(root,l,r,L,R,1);
			}else{
				update(root,l,r,L,R,0);
			}
		}
		if(!tree[root].sum){
			puts("Oh, my god");
			continue;
		}
		query(root,L,R);
		printf("%lld %lld\n",Left,Right);
	}
	return 0;
}

至于线段树离散化的写法,原本以为并不难写,写了之后发现要注意的点很多,开始和上面的动态线段树一样,我选择了维护最长的白色带子和左右两边连续的最长带子,但是之后却发现我忽略了一个很重要的问题,就是在离散化之后白色带子的长度并不能直接比较,线段树比较的使离散化之后的长度,而我们需要的是实际上的长度,然后我选择用一个ll和一个rr来记录左右两边最长带子到的位置,但是并没有想象的那么容易,由于在离散化之后,维护的区间变成了点,我们需要将维护点变为维护区间,因此在离散化时需要将右端点的下标+1;

事实上可以用维护区间的方法代替维护点,以及节点维护的东西可以简单的维护为这个区间内是否颜色单一,如果单一,白色为1,黑色为0,如果不单一为-1,这样的操作不但免去了我之前常用的lazy标记,因为他本身就是一个lazy标记,也省去了我维护一个all标记来表示这个区间颜色是否单一的操作,因为他本身就是一个all标记,他还是一个标记颜色的标记,整个线段树只维护这一个即可,在query的时候遍历整个线段树,暴力计算出离散化之后的点区间内是黑色还是白色,然后跑一边整个带子,即可维护出最长最短路径。

实际上写的时候并没有那么简单,需要注意的细节很多,我相信会有另一种更简便的方法来解决这道题,但是做题不仅仅是为了AC,即使是在一题上绕了圈子,也可以从中学到很多,比如这次将右端点加1变成区间以及用更加巧妙的方法减少维护元素的数量,我原本一直以为线段树的操作麻烦在pushup维护上面,但这一题恰恰可以不用pushup,线段树的难度主要在于思维上的如何使用以及算法上的如何维护。

猜你喜欢

转载自blog.csdn.net/qq_40846926/article/details/81254528
今日推荐