1. 程式人生 > >A Simple Problem with Integers -POJ 3468

A Simple Problem with Integers -POJ 3468

A Simple Problem with Integers

									**POJ 3468**

題目大意
有n個數A1 A2 A3 … An,q個操作。N and Q. 1 ≤ N,Q ≤ 100000.
每個詢問支援兩種操作
C a b c 將Aa 到 Ab都加上c
Q a b 求Aa…Ab的區間和
-1000000000 ≤ Ai ≤ 1000000000.
-10000 ≤ c ≤ 10000.

題目分析
非常簡單的一道線段樹區間修改模板題。
(樹狀陣列也可做只不過更麻煩)
主要是用於入門和練手。
我們用一棵線段樹來維護區間和,對於區間修改的操作,如果我們樸素地使用修改的方式逐一對包含的結點進行更新,那麼最差的情況下可能會導致所有的結點都發生改變而更新,這樣我們修改一次的複雜度就退化到了O(n),顯然是無法接受的。
試想,如果我們在一次修改指令的過程中發現結點p代表的區間[pl,pr]被修改區間[l,r]完全覆蓋,並且逐一更新了子樹p中所有的結點,但是可能在之後的查詢中卻根本不需要用到[pl,pr]的子區間作為候選答案,那麼更新p的所有子樹就是徒勞的。
換句話說,如果當前結點p被完全包含在了[l,r]中,那麼我們可以立即返回,不過我們在返回之前需要在p上留下一個標記,表示該節點曾經被修改過,但它的子節點暫時還未更改

這就是常見的懶惰標記思想。

如果在後續的操作中,我們需要訪問這個結點p的子節點,也就是需要從p向下遞迴,那麼我們可以檢查p上是否含有這個懶惰標記,如果有標記,那麼我們就根據標記資訊更新p的兩個子節點,同時給兩個子節點打上標記,再將p的標記清除。這樣就讓每次的遞迴都是用在了有用的結點上。

換句話說,除了在對於這個區間的操作分成的O(logn)個線段的結點進行直接修改外,
對別的任意的節點的修改都延遲到**在後續操作中遞迴進入到它的父節點並且需要繼續向下遞迴時**再進行。

這樣一來,每次查詢或者修改區間的操作複雜度都是O(nlogn)。

延遲標記提供了線段樹中從上向下傳遞資訊的方法。

同時需要注意的時,一個節點被打上“懶惰標記”的同時,它自身的儲存資訊已經被修改完畢。
同時傳遞完標記並且遞迴完,應該重新計算維護當前節點的資訊。

同時資料較大應該使用long long

程式碼

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

#define LL long long
const int MAXN=100005;
struct Node
{
	LL sum,addv;
}tree[MAXN<<2];
int n,m;

void build_tree(int o,int l,int r)
{
	if(l==r)
	{
		int x;	scanf("%d",&x);
		tree[o].sum=x;
		return;
	}
	int mid=(l+r)>>1;
	build_tree(o<<1,l,mid);
	build_tree(o<<1|1,mid+1,r);
	tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}

void push_down(int o,int l,int r)
{
	if(!tree[o].addv)
		return;
	int lc=(o<<1),rc=(o<<1|1);
	int mid=(l+r)>>1;
	tree[lc].addv+=tree[o].addv;
	tree[rc].addv+=tree[o].addv;
	tree[lc].sum+=tree[o].addv*(mid-l+1);
	tree[rc].sum+=tree[o].addv*(r-mid);
	tree[o].addv=0;
}

LL query(int o,int l,int r,int x,int y)
{
	if(x<=l&&y>=r)
		return tree[o].sum;
	push_down(o,l,r);
	LL ans=0;
	int mid=(l+r)>>1;
	if(x<=mid)
		ans+=query(o<<1,l,mid,x,y);
	if(y>mid)
		ans+=query(o<<1|1,mid+1,r,x,y);
	return ans;
}

void change(int o,int l,int r,int x,int y,int p)
{
	if(x<=l&&y>=r)
	{
		tree[o].sum+=(LL)(r-l+1)*p;
		tree[o].addv+=p;
		return;
	}
	push_down(o,l,r);
	int mid=(l+r)>>1;
	if(x<=mid)
		change(o<<1,l,mid,x,y,p);
	if(y>mid)
		change(o<<1|1,mid+1,r,x,y,p);
	tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
}

void init()
{
	cin>>n>>m;
	build_tree(1,1,n);
}

void work()
{
	char k;
	int x,y,z;
	while(m--)
	{
		cin>>k;
		if(k=='Q')
		{
			scanf("%d%d",&x,&y);
			printf("%lld\n",query(1,1,n,x,y));
		}
		else
		{
			scanf("%d%d%d",&x,&y,&z);
			change(1,1,n,x,y,z);
		}
	}
}

int main()
{
	init();
	work();
	return 0;
}