线段树之成段更新( 需要用到延迟标记,简单来说就是每次更新的时候不要更新到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候)

HDU  1698

链接:  http://acm.hdu.edu.cn/showproblem.php?pid=1698

线段树功能:update:成段替换 (由于只query一次总区间,所以可以直接输出1结点的信息)

<span style="font-size:18px;">#include<iostream>
#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn=100001;
int sum[maxn<<2];
int col[maxn<<2];
void pushUP(int rt)       //当前节点信息更新给父节点 
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt,int m)     //当前节点信息更新给儿子节点 
{
	if(col[rt])
	{
		col[rt<<1]=col[rt<<1|1]=col[rt];
		sum[rt<<1]=(m-(m>>1))*col[rt];
		sum[rt<<1|1]=(m>>1)*col[rt];
		col[rt]=0;
	}
}
void build(int l,int r,int rt)   //建立线段树 
{
	col[rt]=0;
	sum[rt]=1;
	if(l==r)
	return;
	int m=(l+r)/2;
	build(lson);
	build(rson);
	pushUP(rt);
}
void update(int L,int R,int c,int l,int r,int rt)   //成段替换,由于只query一次总区间,所以可以直接输出1节点的信息 
{
	if(L<=l&&r<=R)
	{
		col[rt]=c;
		sum[rt]=(r-l+1)*c;
		return;
	}
	pushdown(rt,r-l+1);
	int m=(l+r)/2;
	if(L<=m)   update(L,R,c,lson);
	if(R>m)   update(L,R,c,rson);
	pushUP(rt);
}
int main()
{
	int T,n,m;
	scanf("%d",&T);
	for(int cas=1;cas<=T;cas++)
	{
		scanf("%d%d",&n,&m);
		build(1,n,1);;
		while(m--)
		{
			int a,b,c;
			scanf("%d%d%d",&a,&b,&c);
			update(a,b,c,1,n,1);
		}
		printf("Case %d: The total value of the hook is %d.\n",cas , sum[1]);
	}	
	return 0;
}</span>

poj 3468

链接:http://poj.org/problem?id=3468

题解:很好的一个区间更新的线段树的模型,线段树的功能,update成段增减,query区间求和,关键在于对pushdown的理解,当扩展区间与节点上区间完全吻合时,停止向下

#include<iostream>
#include<cstdio>
#include<cstring>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn=100001;
long long  sum[maxn<<2],col[maxn<<2];
void pushUP(int rt)
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pushdown(int rt,int m)
{
	if(col[rt])
	{
		col[rt<<1]+=col[rt];
		col[rt<<1|1]+=col[rt];
		sum[rt<<1]+=(m-(m>>1))*col[rt];
		sum[rt<<1|1]+=(m>>1)*col[rt];
		col[rt]=0;
	}
}
void build(int l,int r,int rt)
{
	col[rt]=0;
	if(l==r)
	{
		scanf("%lld",&sum[rt]);
		return;
	}
	int m=(l+r)/2;
	build(lson);
	build(rson);
	pushUP(rt);
}
void update(int L,int R,int c,int l,int r,int rt)   //成段增减 
{
	if(L<=l&&r<=R)
	{
		col[rt]+=c;
		sum[rt]+=c*(r-l+1);
		return;
	}
	pushdown(rt,r-l+1);
	int m=(l+r)/2;
	if(L<=m)   update(L,R,c,lson);
	if(R>m)    update(L,R,c,rson);
	pushUP(rt);
}
long long query(int L,int R,int l,int r,int rt)     //区间求和 
{
	if(L<=l&&r<=R)
	return sum[rt];
	pushdown(rt,r-l+1);
	long long ret=0;
	int m=(l+r)/2;
	if(L<=m)   ret+=query(L,R,lson);
	if(R>m)    ret+=query(L,R,rson);
	return ret;
}
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	build(1,n,1);
	while(q--)
	{
		int a,b,c;
		char op[2];
		scanf("%s",op);
		if(op[0]=='Q')
		{
			scanf("%d%d",&a,&b);
			printf("%lld\n",query(a,b,1,n,1));
		}
		else
		{
			scanf("%d%d%d",&a,&b,&c);
			update(a,b,c,1,n,1);
		}
	}
	return 0;
}

poj2528(线段树+离散化)

题目链接:http://poj.org/problem?id=2528

题目解答:

离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变。举个例子:
有一条1到10的数轴(长度为9),给定4个区间[2,4] [3,6] [8,10] [6,9],覆盖关系就是后者覆盖前者,每个区间染色依次为 1 2 3 4。
现在我们抽取这4个区间的8个端点,2 4 3 6 8 10 6 9
然后删除相同的端点,这里相同的端点为6,则剩下2 4 3 6 8 10 9
对其升序排序,得2 3 4 6 8 9 10
然后建立映射
2     3     4     6     8     9   10
↓     ↓      ↓     ↓     ↓     ↓     ↓
1     2     3     4     5     6     7
那么新的4个区间为 [1,3] [2,4] [5,7] [4,6],覆盖关系没有被改变。新数轴为1到7,即原数轴的长度从9压缩到6,显然构造[1,7]的线段树比构造[1,10]的线段树更省空间,搜索也更快,但是求解的结果却是一致的。
离散化时有一点必须要注意的,就是必须先剔除相同端点后再排序,这样可以减少参与排序元素的个数,节省时间。

由于此题数据较大,所以此题要用离散化

线段树功能:update:成段替换 query:简单hash

#include<iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 11111;
bool hash[maxn];
int li[maxn] , ri[maxn];
int X[maxn*3];
int col[maxn<<4];
int cnt;
void PushDown(int rt)           //更新到儿子节点(此处为覆盖,所以用这种方法更新)
{
	if (col[rt] != -1)
    {
		col[rt<<1] = col[rt<<1|1] = col[rt];
		col[rt] = -1;
	}
}
void update(int L,int R,int c,int l,int r,int rt)      //成段替换
{
	if (L <= l && r <= R) {
		col[rt] = c;
		return ;
	}
	PushDown(rt);
	int m = (l + r) >> 1;
	if (L <= m) update(L , R , c , lson);
	if (m < R) update(L , R , c , rson);
}
void query(int l,int r,int rt)     //简单的hash,cnt统计离散化以后的长度
{
	if (col[rt] != -1)
    {
		if (!hash[col[rt]]) cnt ++;
		hash[ col[rt] ] = true;
		return ;
	}
	if (l == r) return ;
	int m = (l + r) >> 1;
	query(lson);
	query(rson);
}
int Bin(int key,int n,int X[])       //二分查找的过程
{
	int l = 0 , r = n - 1;
	while (l <= r)
    {
		int m = (l + r) >> 1;
		if (X[m] == key) return m;
		if (X[m] < key) l = m + 1;
		else r = m - 1;
	}
	return -1;
}
int main()
{
	int T , n;
	scanf("%d",&T);
	while (T --)
    {
		scanf("%d",&n);
		int nn = 0;
		for (int i = 0 ; i < n ; i ++)   //离散化的过程  
		{
			scanf("%d%d",&li[i] , &ri[i]);
			X[nn++] = li[i];
			X[nn++] = ri[i];
		}
		sort(X , X + nn);
		int m = 1;
		for (int i = 1 ; i < nn; i ++)
		{
			if (X[i] != X[i-1]) X[m ++] = X[i];
		}
		for (int i = m - 1 ; i > 0 ; i --)
		{
			if (X[i] != X[i-1] + 1) X[m ++] = X[i-1] + 1;
		}
		sort(X , X + m);
		memset(col , -1 , sizeof(col));
		for (int i = 0 ; i < n ; i ++)
		{
			int l = Bin(li[i], m, X);
			int r = Bin(ri[i], m, X);
			update(l, r, i, 0, m - 1, 1);
		}
		cnt = 0;
		memset(hash, false, sizeof(hash));
		query(0, m - 1, 1);
		printf("%d\n",cnt);
	}
	return 0;
}


posted @ 2014-05-18 11:12  wolf940509  阅读(141)  评论(0编辑  收藏  举报