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;
}