POJ 3468 A Simple Problem with Integers

题目大意:

        现只有一个测例,给定一个整数序列共N个元素(1 ≤ N ≤ 100,000,其中元素范围为[-1000000000, 1000000000],并且编号为1 ~ N)和Q此询问(1 ≤ Q ≤ 100,000),共有两种询问操作,一种是"C a b c",表示对编号区间[a, b]的每个元素都加上增量c,另一种是"Q a b",表示要求输出编号区间[a, b]中所有元素之和,每次输出占一行。

题目链接

注释代码:

/*                                                     
 * Problem ID : POJ 3468 A Simple Problem with Integers
 * Author     : Lirx.t.Una                                                     
 * Language   : C                                    
 * Run Time   : 1485 ms                                                     
 * Run Memory : 4856 KB                                                     
*/ 

#include <stdio.h>

#define	MAXN		100000

//区间之和可能超过32位
typedef	__int64		llg;

typedef	struct {

	llg		sum;//结点区间之和
	llg		inc;//区间中每个元素的增量

	//左右子树指针
	//不适用完全二叉树保存而减少存储空间
	int		lft;
	int		rht;
} Node;

Node	seg[MAXN << 1];
int		cnt;//指示线段树总共有多少个结点用以拓展结点

void
build( int tree, int lft, int rht ) {
	//sum和inc都为初始化默认值0
	//0为root指针

	int		mid;

	if ( lft != rht ) {//如果到达叶子结点则不再有儿子
		//左右指针指向NULL(默认值)即可
	
		//赋予指针域
		seg[tree].lft = ++cnt;
		seg[tree].rht = ++cnt;

		mid = ( lft + rht ) >> 1;

		build( seg[tree].lft, lft, mid );
		build( seg[tree].rht, mid + 1, rht );
	}
}

void
insert( int tree, int i, int v, int lft, int rht ) {
	//tree结点的区间为[lft, rht]
	//添加元素编号为i,值为v

	int		mid;

	if ( lft == rht ) {//到达叶子结点
	
		seg[tree].sum = v;
		return ;
	}

	seg[tree].sum += v;//没到达叶子结点,则i必定包含在[lft, rht]之中

	mid = ( lft + rht ) >> 1;
	if ( i <= mid ) insert( seg[tree].lft, i, v, lft, mid );
	else insert( seg[tree].rht, i, v, mid + 1, rht );
}

void
update( int tree, int inc, int al, int ar, int lft, int rht ) {
	//[lft, rht]为tree的结点区间
	//al/ar为adding lft/rht,表示对区间[al, ar]中的每个元素都加上增量inc

	int		mid;

	if ( lft == al && ar == rht ) {//命中
	
		seg[tree].inc += inc;
		return ;
	}

	//由于未命中,所以[al, ar]真包含于[lft, rht]
	//由于只有遇到命中的情况才能改变当前结点的inc
	//所以在这里不得不将inc[al, ar]加至当前结点的inc中去
	seg[tree].sum += inc * ( ar - al + 1 );

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid )
		update( seg[tree].lft, inc, al, ar, lft, mid );
	else if ( al > mid )
		update( seg[tree].rht, inc, al, ar, mid + 1, rht );
	else {
	
		update( seg[tree].lft, inc, al, mid, lft, mid );
		update( seg[tree].rht, inc, mid + 1, ar, mid + 1, rht );
	}
}

llg
query( int tree, int al, int ar, int lft, int rht ) {
	//[lft, rht]为tree的结点区间
	//[al, ar]为查询区间

	llg		inc;
	int		mid;

	if ( lft == al && ar == rht )//命中
		return seg[tree].sum + ( rht - lft + 1 ) * seg[tree].inc;

	//未命中,说明[al, ar]在[lft, rht]的子结点中,必须向下查询
	//因此必须将当前结点的inc往下更新到子结点中去
	seg[tree].sum += ( rht - lft + 1 ) * seg[tree].inc;//先将自己的inc加到sum中去
	inc = seg[tree].inc;
	//由于往下更新时两颗子树必定都要更新,因此向下更新时必定第一层就命中
	//因此没有必要调用update函数直接加两行代码就行,可以节省函数调用的时间和空间开销
	seg[ seg[tree].lft ].inc += inc;
	seg[ seg[tree].rht ].inc += inc; 
	seg[tree].inc = 0;//加完之后就清零

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid ) return query( seg[tree].lft, al, ar, lft, mid );
	else if ( al > mid ) return query( seg[tree].rht, al, ar, mid + 1, rht );
	else return
		query( seg[tree].lft, al, mid, lft, mid ) +
	    query( seg[tree].rht, mid + 1, ar, mid + 1, rht );
}

int	
main() {

	int		n, q;//数的总数以及查询次数
	int		v;//临时接受输入的数列中元素的值
	int		lft, rht;//临时接受查询区间

	char	cmd;

	int		i;//计数变量

	scanf("%d%d", &n, &q);
	build( 0, 1, n );

	for ( i = 1; i <= n; i++ ) {
	
		scanf("%d", &v);
		insert( 0, i, v, 1, n );
	}

	while ( q-- ) {

		scanf("\n%c%d%d", &cmd, &lft, &rht);
		switch ( cmd ) {
		
			case 'Q' :

				printf("%I64d\n", query( 0, lft, rht, 1, n));
				break;

			case 'C' :

				scanf("%d", &v);
				update( 0, v, lft, rht, 1, n );
				break;

			default : break;
		}
	}
	
	return 0;
}
无注释代码:

#include <stdio.h>

#define	MAXN		100000

typedef	__int64		llg;

typedef	struct {

	llg		sum;
	llg		inc;

	int		lft;
	int		rht;
} Node;

Node	seg[MAXN << 1];
int		cnt;

void
build( int tree, int lft, int rht ) {

	int		mid;

	if ( lft != rht ) {
	
		seg[tree].lft = ++cnt;
		seg[tree].rht = ++cnt;

		mid = ( lft + rht ) >> 1;

		build( seg[tree].lft, lft, mid );
		build( seg[tree].rht, mid + 1, rht );
	}
}

void
insert( int tree, int i, int v, int lft, int rht ) {

	int		mid;

	if ( lft == rht ) {
	
		seg[tree].sum = v;
		return ;
	}

	seg[tree].sum += v;

	mid = ( lft + rht ) >> 1;
	if ( i <= mid ) insert( seg[tree].lft, i, v, lft, mid );
	else insert( seg[tree].rht, i, v, mid + 1, rht );
}

void
update( int tree, int inc, int al, int ar, int lft, int rht ) {

	int		mid;

	if ( lft == al && ar == rht ) {
	
		seg[tree].inc += inc;
		return ;
	}

	seg[tree].sum += inc * ( ar - al + 1 );

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid )
		update( seg[tree].lft, inc, al, ar, lft, mid );
	else if ( al > mid )
		update( seg[tree].rht, inc, al, ar, mid + 1, rht );
	else {
	
		update( seg[tree].lft, inc, al, mid, lft, mid );
		update( seg[tree].rht, inc, mid + 1, ar, mid + 1, rht );
	}
}

llg
query( int tree, int al, int ar, int lft, int rht ) {

	llg		inc;
	int		mid;

	if ( lft == al && ar == rht )
		return seg[tree].sum + ( rht - lft + 1 ) * seg[tree].inc;

	seg[tree].sum += ( rht - lft + 1 ) * seg[tree].inc;
	inc = seg[tree].inc;
	seg[ seg[tree].lft ].inc += inc;
	seg[ seg[tree].rht ].inc += inc; 
	seg[tree].inc = 0;

	mid = ( lft + rht ) >> 1;

	if ( ar <= mid ) return query( seg[tree].lft, al, ar, lft, mid );
	else if ( al > mid ) return query( seg[tree].rht, al, ar, mid + 1, rht );
	else return
		query( seg[tree].lft, al, mid, lft, mid ) +
	    query( seg[tree].rht, mid + 1, ar, mid + 1, rht );
}

int	
main() {

	int		n, q;
	int		v;
	int		lft, rht;

	char	cmd;

	int		i;

	scanf("%d%d", &n, &q);
	build( 0, 1, n );

	for ( i = 1; i <= n; i++ ) {
	
		scanf("%d", &v);
		insert( 0, i, v, 1, n );
	}

	while ( q-- ) {

		scanf("\n%c%d%d", &cmd, &lft, &rht);
		switch ( cmd ) {
		
			case 'Q' :

				printf("%I64d\n", query( 0, lft, rht, 1, n));
				break;

			case 'C' :

				scanf("%d", &v);
				update( 0, v, lft, rht, 1, n );
				break;

			default : break;
		}
	}

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值