BZOJ 1176: [Balkan2007]Mokia CDQ

本文介绍了一道名为Mokia的比赛题目,该题涉及大规模矩阵的操作,包括修改矩阵元素和查询子矩阵的总权值。通过使用CDQ分治策略,文章详细解释了如何高效地解决这个问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1176: [Balkan2007]Mokia

Time Limit: 30 Sec  Memory Limit: 162 MB
Submit: 2592  Solved: 1161
[Submit][Status][Discuss]

Description

维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.

Input

第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束

Output

对于每个输入2,输出一行,即输入2的答案

Sample Input

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
5

应该这才是BJ真正意义上的CDQ第一题了吧。。

裸三维偏序问题。。

再推荐一遍【教程】简易CDQ分治教程&学习笔记

重点:三元组(时间,横坐标,纵坐标)


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>

using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(int x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=200100;

struct P{int opt,x,y,pos;ll ans;}p[N];

int W;

inline bool cmp(int x,int y)
{return p[x].x==p[y].x?p[x].y==p[y].y?p[x].opt<p[y].opt:p[x].y<p[x].y:p[x].x<p[y].x;}

ll bit[N*10];

inline void modify(int x,int val)
{if(!x)return ;for(;x<=W;x+=(x&-x))bit[x]+=val;}

inline ll query(int x)
{ll res=0;for(;x;x-=(x&-x))res+=bit[x];return res;}

int tot;

int a[N],tmp[N];

void cdq(int l,int r)
{
	if(l==r)return ;
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	register int i=l,j=mid+1;
	while(j<=r)
	{
		while(i<=mid&&p[a[i]].x<=p[a[j]].x){if(p[a[i]].opt==1)modify(p[a[i]].y,p[a[i]].ans);i++;}
		if(p[a[j]].opt>1)p[a[j]].ans+=query(p[a[j]].y);j++;
	}
	while(i>l){i--;if(p[a[i]].opt==1)modify(p[a[i]].y,-p[a[i]].ans);}
	i=l,j=mid+1;int top=l-1;
	while(j<=r||i<=mid)
	{
		if(i>mid)tmp[++top]=a[j++];
		else if(j>r||p[a[i]].x<=p[a[j]].x)tmp[++top]=a[i++];
		else tmp[++top]=a[j++];
	}
	memcpy(a+l,tmp+l,sizeof(int)*(r-l+1));
}

int qnum;

ll ans[N];

int main()
{
	ll S=read();W=read();
	register int opt=read(),x,y,x2,y2,val,i;
	while(opt^3)
	{
		x=read();y=read();
		if(opt==1)
		{val=read();p[++tot]=(P){1,x,y,0,val};}
		else
		{
			x2=read();y2=read();x--,y--;
			p[++tot]=(P){2,x2,y2,++qnum,S*(x2-x)*(y2-y)};
			p[++tot]=(P){2,x,y,qnum,0};
			p[++tot]=(P){3,x,y2,qnum,0};
			p[++tot]=(P){3,x2,y,qnum,0};
		}
		opt=read();
	}
	for(i=1;i<=tot;++i)a[i]=i;
	cdq(1,tot);
	for(i=1;i<=tot;++i)
	if(p[i].opt==2)ans[p[i].pos]+=p[i].ans;
	else if(p[i].opt==3)ans[p[i].pos]-=p[i].ans;
	for(i=1;i<=qnum;++i){print(ans[i]);puts("");}
	return 0;
}
/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

3
5
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值