ZOJ 3324 Machine

题意:

一段区间最开始元素都是0  每次操作可以令一段连续区间+1或-1(在加过的前提下才能减)  每次操作输出整段区间有多少段连续的0

思路:

一看区间操作首先考虑线段树  本题n比较大  但是操作数很小  而且每次操作最多影响一段区间(可用两个数字表示区间头尾)  那么就想到了离散化

我和网上题解的离散方式不同  我的更暴力更无脑- -b

为了保证区间连续性  不能只在线段树节点上记录 1 2 4 7 10 等这样类似的离散值  应该这样表示:

1(0-0) 2(1-1) 3(2-2) 4(3-3) 5(4-4) 6(5-6) 7(7-7) 8(8-9) 9(10-10) 10(11-(n-1))

为了构成这样的序列  我在离散化的时候每次不仅考虑x这个值  还考虑了x-1和x+1  这样就保证了连续(见代码)

然后建树  树上节点中cnt表示区间连续0的段数 val描述该区间被移动的操作 lflag表示区间左边的高度 同理rflag

这里lflag的定义比较奇怪  不能单单用bool表示是否被移动  这里就需要加深理解线段树的“延迟更新”!

因为区间操作[l,r]一旦找到对应位置就不再向叶子节点递归了  也可以这样理解  更新操作在这里被截断了!

这时我们可以说val表示该区间所有截断的更新操作在此区间的影响  那么 lflag = lson.lflag + val 了

这样做可以避免down引起的问题  比如:

0-3(+1) 3-3(+1) 这时0-3的操作就被down下去了  那么0-3(-1)就会使区间的val变成-1而不是一部分为0

这也是定义lflag表示高度的原因

PS:

说说可能不太清晰  自己写几次WA就能体验了…  话说这还是小学弟给我指出的错误  后生可畏啊!

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<iostream>
using namespace std;
#define N 209010
#define L(x) (x<<1)
#define R(x) ((x<<1)|1)
#define Mid(x,y) ((x+y)>>1)

struct input {
	char p[4];
	int l, r;
} in[N];
struct node {
	int l, r, val, cnt, lflag, rflag;
} tree[N * 4];
map<int, int> hash;
int a[N];
int n, m, T, t;

void init(int l, int r, int i) {
	tree[i].l = l;
	tree[i].r = r;
	tree[i].val = tree[i].lflag = tree[i].rflag = 0;
	tree[i].cnt = 1;
	if (l == r)
		return;
	int mid = Mid(l,r);
	init(l, mid, L(i));
	init(mid + 1, r, R(i));
}

void up(int i) {
	if (tree[i].val == 0) {
		if (tree[i].l == tree[i].r)
			tree[i].cnt = 1;
		else {
			tree[i].cnt = tree[L(i)].cnt + tree[R(i)].cnt;
			if (!tree[L(i)].rflag && !tree[R(i)].lflag)
				tree[i].cnt--;
		}
	} else
		tree[i].cnt = 0;
}

void update(int l, int r, int i, int key) {
	if (l == tree[i].l && r == tree[i].r) {
		tree[i].val += key;
		tree[i].lflag += key;
		tree[i].rflag += key;
		up(i);
		return;
	}
	int mid = Mid(tree[i].l,tree[i].r);
	if (r <= mid)
		update(l, r, L(i), key);
	else if (l > mid)
		update(l, r, R(i), key);
	else {
		update(l, mid, L(i), key);
		update(mid + 1, r, R(i), key);
	}
    tree[i].lflag = tree[L(i)].lflag + tree[i].val;
	tree[i].rflag = tree[R(i)].rflag + tree[i].val;
	up(i);
}

int main() {
	int i, len, key, l, r, f;
	scanf("%d", &T);
	for (t = 1; t <= T; t++) {
		scanf("%d%d", &n, &m);
		len = 0;
		for (i = 1; i <= m; i++) {
			scanf("%s %d %d", in[i].p, &in[i].l, &in[i].r);
			a[len++] = in[i].l;
			a[len++] = max(0, in[i].l - 1);
			a[len++] = min(n - 1, in[i].l + 1);
			a[len++] = in[i].r;
			a[len++] = max(0, in[i].r - 1);
			a[len++] = min(n - 1, in[i].r + 1);
		}
		sort(a, a + len);
		hash.clear();
		for (i = 0, f = 1; i < len; i++)
			if (i == 0 || a[i] != a[i - 1])
				hash[a[i]] = f++;
		init(1, f - 1, 1);
		printf("Case #%d:\n", t);
		for (i = 1; i <= m; i++) {
			if (in[i].p[0] == 'p')
				key = 1;
			else
				key = -1;
			l = hash[in[i].l];
			r = hash[in[i].r];
			update(l, r, 1, key);
			cout << tree[1].cnt << endl;
		}
	}
	return 0;
}

ZOJ 3324 Machine,布布扣,bubuko.com

时间: 2024-10-05 18:49:06

ZOJ 3324 Machine的相关文章

ZOJ 3324 Machine(线段树区间合并)

这道题网上很多代码是错误的,由于后台数据水,他们可以AC. 比如这组数据 10 3 p 0 9 r 0 5 r 6 9 输出应该是 0 1 1 所以有的人直接记录该区间是否被覆盖过的方法是错误的 正确方法应该是记录这段区间的最小高度(就是最接近初始位置的高度),和最小高度对应的最长左区间和右区间 开一个sum记录这段区间最小高度的块数,min_v 记录该区间最小高度 cover作为懒惰标记下推该区间的子区间需要被压几次 KinderRiven C Accepted 6776 170 C++ (g

ZOJ 3805 Machine

搜索.... Machine Time Limit: 2 Seconds      Memory Limit: 65536 KB In a typical assembly line, machines are connected one by one. The first machine's output product will be the second machine's raw material. To simplify the problem, we put all machines

ZOJ 3805 Machine(树形DP)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3805 Machine Time Limit: 2 Seconds      Memory Limit: 65536 KB In a typical assembly line, machines are connected one by one. The first machine's output product will be the second mach

ZOJ 3805 Machine(简单dp)

Machine Time Limit: 2 Seconds      Memory Limit: 65536 KB In a typical assembly line, machines are connected one by one. The first machine's output product will be the second machine's raw material. To simplify the problem, we put all machines into a

ZOJ 1364 Machine Schedule(二分图最大匹配)

题意 机器调度问题 有两个机器A,B A有n种工作模式0...n-1 B有m种工作模式0...m-1 然后又k个任务要做 每个任务可以用A机器的模式i或b机器的模式j来完成 机器开始都处于模式0 每次换模式时都要重启 问完成所有任务机器至少重启多少次 最基础的二分图最大匹配问题 对于每个任务把i和j之间连一条边就可以构成一个二分图 那么每个任务都可以对应一条边 那么现在就是要找最少的点 使这些点能覆盖所有的边 即点覆盖数 又因为二分图的点覆盖数 = 匹配数 那么就是裸的求二分图最大匹配问题了 两

zoj 3325 Machine(线段树)

题意:0~n-1的数组,初始值为0:执行m个操作,每次操作执行后输出当前值为0的连续段的段数. 操作1: p i j : i~j区间的每个元素值减1 操作2: r i j :i~j区间的每个元素值加1,每个r操作之前,一定有个相应的p操作 数据范围:1 <= n <= 108, 0 <= m <= 20000 线段树现在已经成了竞赛选手的基本功,但是我这块好弱,写下这个题解,方便自己以后复习. 分析,这很明显是个用线段树来解的题,但是数据范围太大.注意到操作次数最多只有20000次

ZOJ 3407 Doraemon&#39;s Cake Machine [数学]

题意: 最多有2000组测试样例,每组样例代表n,m; n代表要把蛋糕平分的份数,m代表必须进行多少次操作. 一共有三种操作 1.竖切   经过蛋糕圆心,将蛋糕整个向下切. 2.横切   平行于蛋糕平面进行平切. 3.复制某块小蛋糕    这种操作只能在1和2所有操作都进行完才能进行. 求: 最少进行多少次复制操作可以将蛋糕分为一样的恰好n种.注意需要用恰好m次操作. 如果没有可行解输出-1. 思路: 假设进行三种操作的数目分别是xyz.然后连立两个式子把z消掉,get 2x(y+1)-(x+y

ZOJ 1364 POJ 1325 -Machine Schedule

Time Limit:1000MS    Memory Limit:10000K Description As we all know, machine scheduling is a very classical problem in computer science and has been studied for a very long history. Scheduling problems differ widely in the nature of the constraints t

图论 500题——主要为hdu/poj/zoj

转自——http://blog.csdn.net/qwe20060514/article/details/8112550 =============================以下是最小生成树+并查集======================================[HDU]1213   How Many Tables   基础并查集★1272   小希的迷宫   基础并查集★1325&&poj1308  Is It A Tree?   基础并查集★1856   More i