ACM: Copying Data 线段树-成段更新-解题报告

Copying Data
Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u

Description
We often have to copy large volumes of information. Such operation can take up many computer resources. Therefore, in this problem you are advised to come up with a way to copy some part of a number array into another one, quickly.

More formally, you‘ve got two arrays of integers a1, a2, ..., an and b1, b2, ..., bn of length n. Also, you‘ve got m queries of two types:

Copy the subsegment of array a of length k, starting from position x, into array b, starting from position y, that is, execute by + q = ax + q for all integer q(0 ≤ q < k). The given operation is correct — both subsegments do not touch unexistent elements.
Determine the value in position x of array b, that is, find value bx.
For each query of the second type print the result — the value of the corresponding element of array b.

Input
The first line contains two space-separated integers n and m(1 ≤ n, m ≤ 105) — the number of elements in the arrays and the number of queries, correspondingly. The second line contains an array of integers a1, a2, ..., an(|ai| ≤ 109). The third line contains an array of integers b1, b2, ..., bn(|bi| ≤ 109).

Next m lines contain the descriptions of the queries. The i-th line first contains integer ti — the type of the i-th query (1 ≤ ti ≤ 2). If ti = 1, then the i-th query means the copying operation. If ti = 2, then the i-th query means taking the value in array b. If ti = 1, then the query type is followed by three integers xi, yi, ki(1 ≤ xi, yi, ki ≤ n) — the parameters of the copying query. If ti = 2, then the query type is followed by integer xi(1 ≤ xi ≤ n) — the position in array b.

All numbers in the lines are separated with single spaces. It is guaranteed that all the queries are correct, that is, the copying borders fit into the borders of arrays a and b.

Output
For each second type query print the result on a single line.

Sample Input

Input
5 10
1 2 0 -1 3
3 1 5 -2 0
2 5
1 3 3 3
2 5
2 4
2 1
1 2 1 4
2 1
2 4
1 4 2 1
2 2

Output
0
3
-1
3
2
3
-1

这个题目很无语,读题读了块一个小时,发现不知道怎么去成段替换觉得用lazy不适合,后来日天大神跟我讲了一下用lazy的方法,秒懂。

但是写着写着发现有点乱了思路,又重新写了一遍写了1个小时,过了样例后WA了一次,不知道调哪里,然后自己跟着题目注释,突然发现lazy的变化规律越来越清晰。

果然思路混乱就去打注释才是最好的方法!

下面是写了我一个下午加晚上的题目的AC代码:


#include"iostream"
#include"algorithm"
#include"cstdio"
#include"cmath"
#include"cstring"
#define MX 110000
#define INF 0x3f3f3f3f
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
/*

题目没看懂百度了题意和思路:
给你两个数字序列a[]和lazy[],有两种操作,
一种是把起点为xi,长度为k的a[]的一段复制给以yi为起点,长度为k的lazy[]
还有一种操作是询问当前lazy[x]的数字是什么。

思路:
这道题可以用线段树成段更新做,属于染色一类线段树题目
可以这么想,对于任意一个数,记录它有没有被a[]的其中一段覆盖了
如果被覆盖了,可以记录它被覆盖的a[]的那段的起点st以及lazy[]被覆盖的起点ed
如果被覆盖,那么最后结果就是a[x1+ed-st]。

st表示这一线段是否被a[]数组覆盖,如果没有覆盖,那么st=0
如果覆盖,那么被a[]覆盖的区间范围是a[st]~a[st+k-1],ed同理,只不过记录的是lazy[]的开始位置。

对于操作1,输入x1,y1,k,只要使得区间[y1,y1+k-1]的st变成x1,ed变成y1.
对于操作2,输入x1,在线段树中寻找,如果这一点(即[x1,x1])的st是0,就输出c[x1],否则输出a[x1+ed-st].

*/

int a[MX],b[MX];
int st1,st2;
int k;
struct tree {
	int l,r,st,ed;
} lazy[MX<<2];

void Build(int l,int r,int rt) {
	lazy[rt].l=l;		//标记每个的位置
	lazy[rt].r=r;
	lazy[rt].st=lazy[rt].ed=0;    	//清空左右标记
	if(l==r)return;
	int m=(l+r)>>1;
	Build(lson);
	Build(rson);
}

void PushDown(int rt){
	if(lazy[rt].st!=-1) {  		//如果节点发生的变化
		lazy[rt<<1].st=lazy[rt<<1|1].st=lazy[rt].st;	//标记下移
		lazy[rt<<1].ed=lazy[rt<<1|1].ed=lazy[rt].ed;
		lazy[rt].st=lazy[rt].ed=-1;
	}
}

void UpData(int x1,int y1,int l,int r,int rt) {
	if(lazy[rt].st==x1 && lazy[rt].ed==y1)return;  	//如果节点的变化重复,直接返回
	if(lazy[rt].l==l && lazy[rt].r==r) { 		//如果查询到了目标的lazy点
		lazy[rt].st=x1; 				//将lazy点的左右端点更新
		lazy[rt].ed=y1;
		return;
	}
	PushDown(rt);
	int m=(lazy[rt].l+lazy[rt].r)>>1;
	if(r<=m) UpData(x1,y1,l,r,rt<<1); 			//如果中点在右边,向左查询更新
	else if(l>m) UpData(x1,y1,l,r,rt<<1|1); 	//如果中点在左边,向右查询更新
	else {
		UpData(x1,y1,lson);        //如果中点在中间,向两边去更新
		UpData(x1,y1,rson);
	}
}

void Query(int x,int rt) {
	if(lazy[rt].st!=-1) {
		st1=lazy[rt].st; //【一开始用的是 tree 类型函数返回 一个节点,蜜汁出错。最后还是换成全局整型来标记。】
		st2=lazy[rt].ed;
		return;
	}
	int m=(lazy[rt].l+lazy[rt].r)>>1;
	if(x<=m) Query(x,rt<<1);
	else Query(x,rt<<1|1);
}

int main() {
	int n,m,op,x,x1,y1;
	while(~scanf("%d%d",&n,&m)) {
		for(int i=1; i<=n; i++) {
			scanf("%d",&a[i]);
		}
		for(int i=1; i<=n; i++) {
			scanf("%d",&b[i]);
		}
		Build(1,n,1);
		while(m--) {
			scanf("%d",&op);
			if(op==1) {
				scanf("%d%d%d",&x1,&y1,&k);
				UpData(x1,y1,y1,y1+k-1,1);
			} else if(op==2) {
				scanf("%d",&x);
				st1=st2=0;
				Query(x,1);
				if(st1==0) {
					printf("%d\n",b[x]);//如果这点没有标记,则说明没有移动过这点,输出b[aa];
				} else {
					printf("%d\n",a[x-st2+st1]);//否则这点就移动过输出移动区间后的值也就是a[aa-Q.ed+Q.st];
				}
			}
		}
	}
	return 0;
}
  

  

时间: 2024-08-07 04:31:15

ACM: Copying Data 线段树-成段更新-解题报告的相关文章

ZOJ1610_Count the Colors(线段树/成段更新)

解题报告 题意: 一根长度8000的线段上染色,求染完之后,每个颜色在线段上有多少个间断的区间. 思路: 区间问题用线段树,成段的更新区间,最后把所有的区间下压到叶子结点,统计叶子结点的颜色. #include <iostream> #include <cstring> #include <cstdio> using namespace std; int lz[32000],_hash[10000],color[10000],cnt; void push_down(in

POJ训练计划2528_Mayor&#39;s posters(线段树/成段更新+离散化)

解题报告 地址传送门 题意: 一些海报,覆盖上去后还能看到几张. 思路: 第一道离散化的题. 离散化的意思就是区间压缩然后映射. 给你这么几个区间[1,300000],[3,5],[6,10],[4,9] 区间左右坐标排序完就是 1,3,4,5,6,9,10,300000; 1,2,3,4,5,6, 7 ,8; 我们可以把上面的区间映射成[1,8],[2,4],[5,7],[3,6]; 这样就节省了很多空间. 给线段染色, lz标记颜色. #include <map> #include <

POJ训练计划2528_Mayor&amp;#39;s posters(线段树/成段更新+离散化)

解题报告 id=2528">地址传送门 题意: 一些海报,覆盖上去后还能看到几张. 思路: 第一道离散化的题. 离散化的意思就是区间压缩然后映射. 给你这么几个区间[1,300000],[3,5],[6,10],[4,9] 区间左右坐标排序完就是 1,3,4,5,6,9,10,300000; 1,2,3,4,5,6, 7 ,8; 我们能够把上面的区间映射成[1,8],[2,4],[5,7],[3,6]; 这样就节省了非常多空间. 给线段染色, lz标记颜色. #include <ma

Codeforces Round #149 (Div. 2) E. XOR on Segment (线段树成段更新+二进制)

题目链接:http://codeforces.com/problemset/problem/242/E 给你n个数,m个操作,操作1是查询l到r之间的和,操作2是将l到r之间的每个数xor与x. 这题是线段树成段更新,但是不能直接更新,不然只能一个数一个数更新.这样只能把每个数存到一个数组中,长度大概是20吧,然后模拟二进制的位操作.仔细一点就行了. 1 #include <iostream> 2 #include <cstdio> 3 #include <cmath>

POJ 2777 Count Color (线段树成段更新+二进制思维)

题目链接:http://poj.org/problem?id=2777 题意是有L个单位长的画板,T种颜色,O个操作.画板初始化为颜色1.操作C讲l到r单位之间的颜色变为c,操作P查询l到r单位之间的颜色有几种. 很明显的线段树成段更新,但是查询却不好弄.经过提醒,发现颜色的种类最多不超过30种,所以我们用二进制的思维解决这个问题,颜色1可以用二进制的1表示,同理,颜色2用二进制的10表示,3用100,....假设有一个区间有颜色2和颜色3,那么区间的值为二进制的110(十进制为6).那我们就把

POJ 2528 Mayor&#39;s posters (hash+线段树成段更新)

题意:有一面墙,被等分为1QW份,一份的宽度为一个单位宽度.现在往墙上贴N张海报,每张海报的宽度是任意的,但是必定是单位宽度的整数倍,且<=1QW.后贴的海报若与先贴的海报有交集,后贴的海报必定会全部或局部覆盖先贴的海报.现在给出每张海报所贴的位置(左端位置和右端位置),问张贴完N张海报后,还能看见多少张海报?(PS:看见一部分也算看到.) 思路:简单的成段更新,但是数据量是1千万,会MT,所以要区间压缩(离散化),保证覆盖的关系不变,离散化的时候有个易错的细节,poj数据水了,这个易错点引用h

POJ训练计划2777_Count Color(线段树/成段更新/区间染色)

解题报告 题意: 对线段染色,询问线段区间的颜色种数. 思路: 本来直接在线段树上染色,lz标记颜色.每次查询的话访问线段树,求出颜色种数.结果超时了,最坏的情况下,染色可以染到叶子节点. 换成存下区间的颜色种数,这样每次查询就不用找到叶子节点了,用按位或来处理颜色种数. #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace

Light OJ 1411 Rip Van Winkle`s Code 线段树成段更新

题目来源:Light OJ 1411 Rip Van Winkle`s Code 题意:3中操作 1种查询 求区间和 其中每次可以把一段区间从左到右加上1,2,3,...或者从右到左加上...3,2,1 或者把某个区间的数都置为v 思路:我是加了6个域 add是这段区间每个数都要加上add  add是这么来的 对与123456...这个等差数列 可能要分为2个区间 那么我就分成123和123 两个右边的等差数列每个数还应该加上3 所以右区间add加3 v是这个区间都要置为v 他的优先级最高 b是

poj 3468 A Simple Problem with Integers (线段树成段更新)

A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 77486   Accepted: 23862 Case Time Limit: 2000MS Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of