description
你有乙個n*n的棋盤,每個格仔內有乙個整數,初始時的時候全部為0,現在需要維護兩種操作:
1 x y a(1<=x,y<=n,a是正整數):將格仔x,y裡的數字加上a
2 x1 y1 x2 y2(1<=x1<= x2<=n,1<=y1<= y2<=n):輸出x1 y1 x2 y2這個矩形內的數字和
3:終止程式
input
輸入檔案第一行乙個正整數n,接下來每行乙個操作
output
對於每個2操作,輸出乙個對應的答案
sample input
4 1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3sample output
3 5
solution
以sum(x,y)表示棋盤上所有在點(x,y)左上方的元素之和,那麼對於每一次查詢操作x1,y1,x2,y2,答案即為sum(x2,y2)+sum(x1-1,y1-1)+sum(x1-1,y2)-sum(x2,y1-1),將每次查詢操作看作在棋盤上(x1-1,y2),(x2,y1-1),(x1-1,y1-1),(x2,y2)四個點插入值為0的點,那麼問題轉化為對於每個插入操作,如何求在此操作之前插入的點中處於該點左上方的點權之和,這個三維偏序關係的統計可以通過cdq分治來實現,按插入順序分治,ans[i]表示插入的第i個元素的查詢結果,對[l,mid]和[mid+1,r]中的元素都以x為第一關鍵字公升序排,以y為第二關鍵字公升序排,對於[mid+1,r]中的每個i,將區間[l,mid]中所有滿足j.y<=i.y的j以j.x為下標,j.num為鍵值插入到樹狀陣列中,那麼累加樹狀陣列中所有下標小於等於i.x的元素鍵值之和累加到ans[i]中即可,最後再根據給每個操作打的標記(標記其屬於第幾次查詢)和其對答案的貢獻狀態(+或-)計算每次查詢的結果即可
code
#include
#include
#include
#include
using
namespace
std;
#define maxn 1111111
int n,res,tot,ans[maxn];
struct node
; node(int x,int y,int num,int id,int sta,int flag,int ans):x(x),y(y),num(num),id(id),sta(sta),flag(flag),ans(ans){};
bool
operator
<(const node &a)const
void update(int x,int v)
}int query(int x)
return ans;
}}bit;
void cdq(int l,int r)
for(int i=l;iif(p[i].num)bit.update(p[i].x,-p[i].num);
merge(p+l,p+mid+1,p+mid+1,p+r+1,q);
for(int i=0;i1;i++)p[l+i]=q[i];
}int main()
else
if(op==2)
}
cdq(1,res);
for(int i=1;i<=res;i++)
ans[p[i].flag]+=p[i].sta*p[i].ans;
for(int i=1;i<=tot;i++)
printf("%d\n",ans[i]);
}return
0;}
BZOJ 2683 簡單題 CDQ分治
n n矩陣,支援單點修改,查詢某乙個子矩陣內的和 n leq 500000 運算元 leq 200000 首先運用二維字首和的思想,把子矩陣的和拆成四個字首和。然後把詢問和修改看成 x,y,t 的三元組,t表示當前是第幾次操作。然後就變成三維偏序問題,對於每個詢問,找x,y,t均比它小的修改操作,再...
bzoj2683簡單題 cdq分治
time limit 50 sec memory limit 128 mb submit 1803 solved 731 submit status discuss 你有乙個n n的棋盤,每個格仔內有乙個整數,初始時的時候全部為0,現在需要維護兩種操作 命令引數限制 內容1 x y a 1 x,y ...
bzoj 2683 簡單題 cdq分治 樹狀陣列
要求資瓷下列操作 1 x y z把 x,y 加上z 2 x1 y1 x2 y2求矩形x1 y1 x2 y2的權值和 n 500000,x,y n,q 200000 首先可以把乙個詢問用容斥原理拆成四個詢問,然後在cdq分治中二分乙個橫座標mid,然後把操作從前往後掃一遍,若是修改操作且橫座標不大於m...