LA-11992-Fast Matrix Operations 快速矩阵操作(线段树成段更新)

这篇博客介绍了一个使用线段树处理矩阵操作的问题,矩阵大小限制为r*c,r≤20, r*c≤1e6,初始元素全为0。提供了三种操作:1) 子矩阵元素加val;2) 子矩阵元素设为val;3) 查询子矩阵元素的sum、max和min。解决方案是为每一行建立一个线段树,维护sum、max和min信息,并确保在add操作前处理set标记。博主提到代码在VJ上运行时间为600ms,表现良好。" 102646127,8538151,Ubuntu 18.04 安装及配置 Oracle VirtualBox 和 phpVirtualBox,"['Ubuntu虚拟化安装', 'Oracle VirtualBox', 'phpVirtualBox']
摘要由CSDN通过智能技术生成

题意:

给定一个r*c(r<=20,r*c<=1e6)的矩阵,其元素都是0,现在对其子矩阵进行操作。

1 x1 y1 x2 y2 val 表示将(x1,y1,x2,y2)(x1<=x2,y1<=y2)子矩阵中的所有元素add上val;

2 x1 y1 x2 y2 val 表示将(x1,y1,x2,y2)(x1<=x2,y1<=y2)子矩阵中的所有元素set为val;

3 x1 y1 x2 y2 val 表示输出(x1,y1,x2,y2)(x1<=x2,y1<=y2)子矩阵中的所有元素的sum,最大最小值max,min

线段树基本功能啦。。开r个线段树..(最多20).每次对r个线段树操作就好了

这里有个问题就是,set和add的处理,  要知道set和add不可能同时存在于一个节点中

所以,每次add操作之前都要把当前节点的set标记 推送到 他的儿子中

维护好 sum ,max,min三个信息就可以了

对于查询,写了三个函数,

注意  query_max中 非法区间要返回0,而query_min中非法区间要返回inf 

一开始memset SET数组为-1 表示当前节点无set标记


全程无槽点..1A了...   跑了600ms、留在了VJ第一页、算不错啦。。。上一个线段树代码跑了全VJ倒数第二大哭


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map> 
#include <vector>
using namespace std;
#define inf 2147483647
const int N = 1000050;
int  sum[21][2*N], add[21][2*N], SET[21][2*N];
int  maxx[21][2*N], minn[21][2*N];

int max(int a,int b)
{return a<b?b:a;}
int min(int a,int b)
{return a>b?b:a;}	
 
void pushDown(int i, int l, int r,int num,int op)		//把i节点的延迟标记传递到左右儿子节点
{
	if (op==2)
	{
		if (SET[num][i]!=-1)
		{
			int mid = (l + r) >> 1;
			SET[num][i << 1] = SET[num][i];
			sum[num][i << 1] = (mid - l + 1) * SET[num][i];  //[l, mid]代表左儿子区间
			SET[num][i << 1 | 1] = SET[num][i];
			sum[num][i << 1 | 1] = (r - mid) * SET[num][i];  //[mid + 1, r]代表右儿子区间
			minn[num][i<<1]=maxx[num][i<<1]=maxx[num][i<<1|1]=minn[num][i<<1|1]=SET[num][i];
			add[num][i<<1]=add[num][i<<1|1]=0;
				SET[num][i] = -1;
			
		}
	}
	else
		if (op==1)
		{
			if(add[num][i] != 0)
			{ 
				int mid = (l + r) >> 1;	
				pushDown(i<<1,l,mid,num,2);
				pushDown(i<<1|1,mid+1,r,num,2);//把儿子的SET传下去

				add[num][i << 1 | 1] += add[num][i];
				add[num][i << 1] += add[num][i];
				sum[num][i << 1] += (mid - l + 1) * add[num][i];  //[l, mid]代表左儿子区间
				sum[num][i << 1 | 1] += (r - mid) * add[num][i];  //[mid + 1, r]代表右儿子区间
				minn[num][i<<1]+=add[num][i];
				maxx[num][i<<1]+=add[num][i];
				maxx[num][i<<1|1]+=add[num][i];
				minn[num][i<<1|1]+=add[num][i];
				add[num][i] = 0;
			}
		}
}

void update(int i, int l, int r, int ql, int qr, int val,int num,int op) //更新区间为qlqr,当前区间为l,r,代表当前区间和的节点为i,更新值为val,op为1是add,2是SET
{
    if(l > qr || ql > r)	
		//更新区间不在当前区间内
        return ;
    if(l >= ql && r <= qr)	//要更新的区间把当前区间完全包括,则把当前整个区间+val,然后返回上一层
	{	
		if (op==1)  //add
		{
			if (SET[num][i]!=-1)
				pushDown(i,l,r,num,2);//add之前先把SET传下去
 
			sum[num][i] += (r - l + 1) * val;
			maxx[num][i]+=val;
			minn[num][i]+=val;
			add[num][i]+=val;
        }
		else	
			if (op==2)			//SET
			{
				add[num][i]=0;
				sum[num][i]= (r - l + 1) * val;
				maxx[num][i]=val;
				minn[num][i]=val;
				SET[num][i]=val;
			}
			return ;
    }
				//如果上面没reutrn 表示要往左右儿子区间update,所以把延迟标记放下去
	pushDown(i,l,r,num,2);	//先传SET
		pushDown(i, l, r,num,1);	//再传add
	
	
    int mid = (l + r) >> 1;
    update(i << 1, l, mid, ql, qr, val,num,op);
    update(i << 1 | 1, mid + 1, r, ql, qr, val,num,op);

    sum[num][i] = sum[num][i << 1] + sum[num][i << 1 | 1];
	maxx[num][i] = max(maxx[num][i << 1] , maxx[num][i << 1 | 1]);
	minn[num][i] = min(minn[num][i << 1] , minn[num][i << 1 | 1]);


}

int query(int i, int l, int r, int ql, int qr,int num)	 //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i 
{
    if(l > qr || ql > r)
        return 0;
    if(l >= ql && r <= qr)
        return sum[num][i];

	pushDown(i,l,r,num,2);	//先传SET
		pushDown(i, l, r,num,1);	//再传add
    int mid =( l + r) >> 1;
    return query(i << 1, l, mid, ql, qr,num)   + query(i << 1 | 1, mid + 1, r, ql, qr,num);
}
int query_max(int i, int l, int r, int ql, int qr,int num)	 //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i 
{
    if(l > qr || ql > r)
        return 0;
    if(l >= ql && r <= qr)
        return maxx[num][i];

	pushDown(i,l,r,num,2);	//先传SET
		pushDown(i, l, r,num,1);	//再传add
    int mid =( l + r) >> 1;
    return max(query_max(i << 1, l, mid, ql, qr,num)   , query_max(i << 1 | 1, mid + 1, r, ql, qr,num));
}
int query_min(int i, int l, int r, int ql, int qr,int num)	 //查询区间为qlqr,当前区间为l,r,代表当前区间和的节点为i 
{
    if(l > qr || ql > r)
        return inf;
    if(l >= ql && r <= qr)
        return minn[num][i];

	pushDown(i,l,r,num,2);	//先传SET
		pushDown(i, l, r,num,1);	//再传add
    int mid =(  l + r) >> 1;
 return min(query_min(i << 1, l, mid, ql, qr,num)   , query_min(i << 1 | 1, mid + 1, r, ql, qr,num));
}
int main()
{
	int op;
    int r,c,m,i,j;
	int x1,x2,y1,y2,v;
	while(    scanf("%d%d%d", &r,&c,&m)!=EOF)
	{
		memset(sum,0,sizeof(sum));
		memset(add,0,sizeof(add));
		memset(maxx,0,sizeof(maxx));
		memset(minn,0,sizeof(minn));
		memset(SET,-1,sizeof (SET));
		for(  i = 1; i <= m; i++)
		{
			scanf("%d",&op); 
			if (op==1)
			{
				scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&v);
				for (j=x1;j<=x2;j++)
				{
					update(1,1,c,y1,y2,v,j,1);
				}
			}
			else
				if (op==2)
				{	
					scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&v); 
					for (j=x1;j<=x2;j++)
					{
						update(1,1,c,y1,y2,v,j,2);
					}
					
				}
				else
				{
					scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 
					int ans1=0;
					int ans2=0;
					int ans3=inf;
					for (j=x1;j<=x2;j++)
					{
						ans1+=query(1,1,c,y1,y2,j); 
						ans2=max(ans2,query_max(1,1,c,y1,y2,j));
						ans3=min(ans3,query_min(1,1,c,y1,y2,j)); 
					}
					printf("%d %d %d\n",ans1,ans3,ans2);
					
				}
				
		}
		
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值