Mokia 摩基亚

题目链接:[https://www.luogu.com.cn/problem/P4390]

快捷版题意:

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

思路:

第一反应二维树状数组,然而看到\(W<=2000000\)便望而却步。
想到今天在练\(cdq\)分治,于是往这个方向靠。
首先,容易想到子矩阵总权值可拆分为四个前缀子矩阵权值和。
那么,前缀子矩阵权值和权值和怎么求呢?
初始值\(S\)直接加就可以了。
考虑到一个修改操作对求和有贡献,当且仅当它在需要求的前缀子矩阵内部。
设前缀子矩阵右下角坐标为\((x,y)\),则对于对其有贡献的点P,满足\(xp<=x\)\(yp<=y\)
发现这原来就是一个裸的三维偏序,上cdq分治即可。

注意事项:

  • 树状数组查询时可能会有0,要特判一下。
  • 数组要开大些,因为一个询问会被分裂长四个。
  • 还有变量名不要手残打错。

code:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+50;
const int W=2e6+50;
int s,w,n;
struct node{int x,y,v,tp,id,ans;}a[N],b[N];
bool operator<(node x,node y){return x.x<y.x;}
inline int read()
{
	int s=0,w=1; char ch=getchar();
	for(;!isdigit(ch);ch=getchar())if(ch=='-')w=-1;
	for(;isdigit(ch);ch=getchar())s=(s<<1)+(s<<3)+(ch^48);
	return s*w;
}
struct tree{
	int c[W];
	inline int lowbit(int x){return x&(-x);}
	inline void add(int x,int v)
	{
		if(!x) return;
		for(;x<=w;x+=lowbit(x))c[x]+=v;
	}
	inline int query(int x)
	{
		if(!x) return 0;
		int ans=0;
		for(;x;x-=lowbit(x))ans+=c[x];
		return ans;
	}
}T;
void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid);cdq(mid+1,r);
	sort(b+l,b+mid+1);sort(b+mid+1,b+r+1);
	int i=l,j=mid+1;
	for(;j<=r;++j)
	{
		for(;i<=mid&&b[i].x<=b[j].x;++i)
			if(b[i].tp==1) T.add(b[i].y,b[i].v);
		if(b[j].tp==2)
			a[b[j].id].ans+=T.query(b[j].y);
	}
	for(int e=l;e<i;++e)
		if(b[e].tp==1) T.add(b[e].y,-b[e].v);
}
int main()
{
	s=read(),w=read();
	while(7)
	{
		int opt=read();
		if(opt==3) break;
		if(opt==1)
			a[++n].x=read(),a[n].y=read(),a[n].v=read(),a[n].tp=1,a[n].id=n;
		else
		{
			int x11=read(),y11=read(),x22=read(),y22=read();
			a[++n].x=x22,a[n].y=y22,a[n].tp=2,a[n].id=n;
			a[++n].x=x11-1,a[n].y=y22,a[n].tp=2,a[n].id=n;
			a[++n].x=x22,a[n].y=y11-1,a[n].tp=2,a[n].id=n;
			a[++n].x=x11-1,a[n].y=y11-1,a[n].tp=2,a[n].id=n;
		}
	}
	for(int i=1;i<=n;++i) b[i]=a[i];
	cdq(1,n);
	for(int i=1;i<=n;++i)
		if(a[i].tp==2)
		{
			int bas=abs(a[i+1].x-a[i+2].x)*abs(a[i+1].y-a[i+2].y)*s;
			printf("%d\n",bas+a[i].ans-a[i+1].ans-a[i+2].ans+a[i+3].ans);
			++i,++i,++i;
		}
	return 0;
}
posted @ 2019-11-30 15:42  BILL666  阅读(139)  评论(0编辑  收藏  举报