cunzai_zsy0531

关注我

P4390 [BOI2007]Mokia 题解

题面

平面上两个操作:给一些坐标加权和查询矩形点权和。坐标 \(x,y\leq 2\times 10^6\),加权操作最多 \(1.6\times 10^5\) 个,查询操作最多 \(10000\) 个。

感觉时间和 \(x,y\) 大概能构成个三维偏序,直接上 cdq 分治。矩形查询看起来不是很好,考虑把它在 \(y\) 方向上拆成 \(y2-(y1-1)\) 两个查询的答案相减。这样就完全转化为一个三维偏序问题,直接树状数组维护一下前缀和就可以轻松 \(O(n\log^2 n)\) 解决问题。注意 cdq 里边按照什么排序,还有树状数组的清空。

点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=4e5+13,M=2e6+13;
struct Node{
	int x,y,z,id,sum;
}a[N],b[N];
int n,m,ans[N];
struct BIT{
	int t[M];
	#define lowbit(x) ((x)&(-x))
	inline void add(int x,int k){for(;x<=m;x+=lowbit(x))t[x]+=k;}
	inline int sum(int x){int res=0;for(;x;x-=lowbit(x))res+=t[x];return res;}
	#undef lowbit
}T;
void cdq(int l,int r){
	if(l==r) return;
	int mid=(l+r)>>1;
	cdq(l,mid),cdq(mid+1,r);
	int j=l;
	for(int i=mid+1;i<=r;++i){
		if(!a[i].id) continue; 
		while(j<=mid&&a[j].y<=a[i].y){
			if(!a[j].id) T.add(a[j].x,a[j].z);
			++j;
		}
		a[i].sum+=T.sum(a[i].z)-T.sum(a[i].x-1);
	}
	for(int i=l;i<j;++i)
		if(!a[i].id) T.add(a[i].x,-a[i].z);
	int l1=l,l2=mid+1,tot=0;
	while(l1<=mid||l2<=r){
		if(l1>mid) b[++tot]=a[l2],++l2;
		else if(l2>r) b[++tot]=a[l1],++l1;
		else{
			if(a[l1].y<=a[l2].y) b[++tot]=a[l1],++l1;
			else b[++tot]=a[l2],++l2;
		}
	}
	for(int i=l;i<=r;++i) a[i]=b[i-l+1];
}
int main(){
	int op,cnt=0;
	scanf("%d%d",&op,&m);
	while(scanf("%d",&op)==1&&op!=3){
		++n;
		if(op==1) scanf("%d%d%d",&a[n].x,&a[n].y,&a[n].z);
		else{
			a[n].id=(++cnt)*(-1);
			scanf("%d%d%d",&a[n].x,&a[n].y,&a[n].z);a[n].y--;
			a[++n].id=cnt;
			scanf("%d",&a[n].y);a[n].x=a[n-1].x,a[n].z=a[n-1].z;
		}
	}
	cdq(1,n);
	for(int i=1;i<=n;++i)
		if(a[i].id>0) ans[a[i].id]+=a[i].sum;
		else if(a[i].id<0) ans[-a[i].id]-=a[i].sum;
	for(int i=1;i<=cnt;++i) printf("%d\n",ans[i]);
	return 0;
}
posted @ 2022-05-18 20:06  cunzai_zsy0531  阅读(17)  评论(0编辑  收藏  举报