UVA - 11992 Fast Matrix Operations (线段树 + 懒标记)

题目链接

分析

这个我们看一下数据范围,矩阵最多只有20行;
那我们就不用二维线段树了;

对于每一行开一个线段树,分别维护就行;
对于每一个查询和修改x1,x2,y1,y2,我们分别对每一行的线段树处理就行;
时间复杂度O(qnlog(m))

那么问题就转换成,对于一颗线段树,如何维护:
区间变成一个数,区间+一个数,区间查询最值和矩阵和?
其实这个题之前写过了:《迪拜的超市》
题目连接

题解看这里

下面还是简单分析一下:

  1. 需要维护哪些东西?

     l, r, sum, mx, mn, add, flag(区间置为flag)
    
  2. 如何维护信息?
    (1)pushup时: 这个简单,区间和,区间最值

     (
     	有多个懒标记了, 要考虑 懒标记之间的影响 和 懒标记的优先级 (最关键)
     	这里就不要具体分析了,直接进入poushdown 和 打懒标记时 的分析;
     	因为这两个是不同的,pushdown时懒标记有后效性,而打懒标记时没有;
     	统一分析懒标记之间的影响不太好...
     )
    
     改成下面这样分析:
    

    (2). 直接考虑pushdown怎么维护?

     (此时懒标记对于当前节点是有后效性的,要注意!)
      然后考虑pushdown中懒标记的优先级。
    

    (3). 再考虑打懒标记时要怎么维护?(这个懒标记是没有后效性的!)

     这个后效性参考《迪拜的超市》的pushdown;
    

代码

#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
#include<stdio.h>
#include<map>
#include<algorithm>
#include<deque>
#include<stack>
#include<set>
#include <unordered_map>
#include<math.h>
#include<string.h>
#define IOS ios::sync_with_stdio(false),cin.tie(0);
using namespace std;
 
#define pb push_back
#define coutl cout<<"------------"<<endl;
#define fi first
#define se second
  
#define ire(x) scanf("%d",&x)
#define iire(a,b) scanf("%d %d",&a,&b)
#define lre(x) scanf("%lld",&x)
#define llre(a,b) scanf("%lld %lld",&a,&b)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define endl "\n"
#define PI acos(-1.0)

typedef long long ll;
typedef unsigned long long ull;
      
typedef pair<int, int> PII;
typedef pair<double, int> PDI;
typedef pair<ll, ll> PLL;
typedef pair<double, double> PDD;
typedef pair<double, pair<int, double>> PDID;
typedef pair<char, char> PCC;
typedef pair<char, pair<int, int> > PCII;
typedef pair<int, pair<int, int> > PIII;
typedef pair<int, pair<int, pair<int, int> > > PIIII;
typedef pair<ll, pair<int, int> > PLII;
 
const int maxn = 5e5 + 7;
const int N = 2010 + 7;
const int M = 1e6 + 7;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double pi = acos(-1);
const double eps = 1e-8;
  
ll gcd(ll a,ll b) {return b==0 ? a : gcd(b,a%b);}
ll lcm(ll a,ll b) {return a*b / gcd(a,b);}
ll qmi(ll a,ll b,ll p) {ll ans = 1; while(b) { if(b & 1) ans = ans * a % p; a = a * a % p; b >>= 1; } return ans;}
int lowbit(int x) {return x & (-x);}

struct node
{
	int l,r;
	ll sum;
	int mx;
	int mn;

	ll add;		//区间+懒标记
	int flag;	//区间覆盖懒标记(初始化为-1)
	
}tr[22][maxn * 4];

int n,m,q;
ll sum;
int mx,mn;

void pushup(int u,int id)
{
	tr[id][u].sum = tr[id][u<<1].sum + tr[id][u<<1|1].sum;
	tr[id][u].mx = max(tr[id][u<<1].mx , tr[id][u<<1|1].mx);
	tr[id][u].mn = min(tr[id][u<<1].mn , tr[id][u<<1|1].mn);
}

//区间覆盖的标记优先级比较大
void pushdown(int u,int id)
{
	if(tr[id][u].flag != -1)	//先更新cl标记, 需要清空
	{
		tr[id][u<<1].flag = tr[id][u].flag;
		tr[id][u<<1].sum = (ll)(tr[id][u<<1].r - tr[id][u<<1].l + 1) * tr[id][u].flag;
		tr[id][u<<1].mn = tr[id][u<<1].mx = tr[id][u].flag;
		tr[id][u<<1].add = 0;
		
		tr[id][u<<1|1].flag = tr[id][u].flag;
		tr[id][u<<1|1].sum = (ll)(tr[id][u<<1|1].r - tr[id][u<<1|1].l + 1) * tr[id][u].flag;
		tr[id][u<<1|1].mn = tr[id][u<<1|1].mx = tr[id][u].flag;
		tr[id][u<<1|1].add = 0;
		
		tr[id][u].flag = -1;
	}
	
	if(tr[id][u].add)	//需要加
	{
		tr[id][u<<1].add += tr[id][u].add;
		tr[id][u<<1].sum += (ll)(tr[id][u<<1].r - tr[id][u<<1].l + 1) * tr[id][u].add;
		tr[id][u<<1].mx += tr[id][u].add;
		tr[id][u<<1].mn += tr[id][u].add;
		
		tr[id][u<<1|1].add += tr[id][u].add;
		tr[id][u<<1|1].sum += (ll)(tr[id][u<<1|1].r - tr[id][u<<1|1].l + 1) * tr[id][u].add;
		tr[id][u<<1|1].mx += tr[id][u].add;
		tr[id][u<<1|1].mn += tr[id][u].add;
		
		tr[id][u].add = 0;
	}
}

void build(int u,int l,int r,int id)	//建树
{
	tr[id][u] = {l,r};
	tr[id][u].flag = -1;
	
	if(l == r)
	{
		tr[id][u] = {l,r,0,0,0,0,-1};
		return;
	}
	
	int mid = l + r >> 1;
	build(u<<1, l, mid,id);
	build(u<<1|1, mid+1, r,id);
	
	pushup(u,id);
}

void update1(int u,int l,int r,int v,int id)   //区间+x
{
	if(l > r) return;
	if(tr[id][u].l >= l && tr[id][u].r <= r)
	{
		//注意add是没有包括自己区间的哦
		tr[id][u].sum += (ll)(tr[id][u].r - tr[id][u].l + 1) * v;
		tr[id][u].mx += v;
		tr[id][u].mn += v;
		tr[id][u].add += v;
		
		return;
	}
	
	pushdown(u,id);	//分裂了,下传懒标记
	
	int mid = tr[id][u].l + tr[id][u].r >> 1;
	if(l <= mid) update1(u<<1, l, r, v,id);
	if(r > mid) update1(u<<1|1, l, r, v,id);
	
	pushup(u,id);	//向上更新
}

void update2(int u,int l,int r,int v,int id)   //区间+x
{
	if(l > r) return;
	if(tr[id][u].l >= l && tr[id][u].r <= r)
	{
		//注意add是没有包括自己区间的哦
		tr[id][u].sum = (ll)(tr[id][u].r - tr[id][u].l + 1) * v;
		tr[id][u].mx = v;
		tr[id][u].mn = v;
		
		tr[id][u].flag = v;
		tr[id][u].add = 0;
		
		return;
	}
	
	pushdown(u,id);	//分裂了,下传懒标记
	
	int mid = tr[id][u].l + tr[id][u].r >> 1;
	if(l <= mid) update2(u<<1, l, r, v,id);
	if(r > mid) update2(u<<1|1, l, r, v,id);
	
	pushup(u,id);	//向上更新
}

void query(int u,int l,int r,int id)
{
	if(tr[id][u].l >= l && tr[id][u].r <= r)
	{
		sum += tr[id][u].sum;
		mx = max(mx,tr[id][u].mx);
		mn = min(mn,tr[id][u].mn);
		
		return;
	}
	
	pushdown(u,id);	//分裂了,下传懒标记
	
	int mid = tr[id][u].l + tr[id][u].r >> 1;

	if(l <= mid) query(u<<1, l, r, id);
	if(r > mid) query(u<<1|1, l, r, id);
}

int main()
{
	while(scanf("%d %d %d",&n,&m,&q) != EOF)
	{
		for(int i=1;i<=n;i++) build(1,1,m,i);	//每一行建树
		
		while(q--)
		{
			int op;
			ire(op);
			
			int x1,x2,y1,y2,val;
			
			if(op == 1)	//区间+
			{
				iire(x1,y1);
				iire(x2,y2);
				ire(val);
				
				for(int i=x1;i<=x2;i++) update1(1,y1,y2,val,i);
			}
			else if(op == 2)	//区间覆盖
			{
				iire(x1,y1);
				iire(x2,y2);
				ire(val);
				
				for(int i=x1;i<=x2;i++) update2(1,y1,y2,val,i);
			}
			else	//矩阵查询
			{
				iire(x1,y1);
				iire(x2,y2);
				
				//初始化
				sum = 0; mn = inf; mx = -inf;
				
				for(int i=x1;i<=x2;i++) query(1,y1,y2,i);
				
				printf("%lld %d %d\n",sum,mn,mx);
			}
		}
	}

	return 0;
}


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值