樹狀陣列題 1

2022-06-03 06:39:07 字數 2178 閱讀 2433

給你乙個\(n*n\)的矩陣,不用算矩陣乘法,但是每次詢問乙個子矩形的第\(k\)小數。

第一行兩個數\(n,q\),表示矩陣大小和詢問組數;

接下來\(n\)行\(n\)列一共\(n*n\)個數,表示這個矩陣;

再接下來\(q\)行每行5個數描述乙個詢問:\(x1,y1,x2,y2,k\)

表示找到以\((x1,y1)\)為左上角、以\((x2,y2)\)為右下角的子矩形中的第\(k\)小數。

對於每組詢問輸出第\(k\)小的數。

2 2

2 13 4

1 2 1 2 1

1 1 2 2 3

1

3

\[n<=500,q<=60000

\]這是一道整體二分的經典題目。

這道題顯然可以給每個詢問二分答案,統計該詢問矩陣中小於等於mid的元素個數。如果大於等於k,說明猜大了,否則說明猜小了。

如果用這種方法的話,對於每個詢問都至少要用o(詢問矩陣大小*log值域)的時間複雜度解決,多組詢問的話時間不能接受。

發現多個詢問的二分答案是可以同時被檢驗的,我們可以為所有詢問同時二分答案,把所有答案小於等於mid的詢問放在詢問序列的左側,大於mid的放到詢問序列的右側然後遞迴處理。

這樣為什麼會快呢?我們每次可以用把矩陣中小於等於mid的元素染成黑色,剩下的元素保持白色。這樣對於每乙個詢問的檢驗,就相當於是統計某個子矩陣中黑點的個數。為什麼不用二維樹狀陣列維護字首和呢?

最開始的時候整個矩陣都設為0,然後讓所有小於等於mid的位置加1,\(o(n^2\log^2n)\)

n)處理完整個矩陣之後再\(o(\log^2n)\)去應付每乙個詢問。被詢問的子矩陣中可能有很多重疊的部分,這樣保證了在每次詢問過程中,矩陣中的每個元素只對執行時間做一次貢獻,所以這個演算法要比對於每個詢問單獨二分要快得多。

這樣做的時間複雜度為什麼是對的呢?我們是在二分值域,考慮二分過程中的每一層對答案的貢獻。

對於每一層二分,矩陣中的每個元素最多被加入樹狀陣列一次。

對於每一層二分,每個詢問只會被處理一次。

二分值域的過程中最多隻會出現\(o(\log n)\)層。

時間複雜度\(o((n^2 + q)\log^3)\)

#include #define int long long

#define re register int

using namespace std;

inline void read(int &x)

inline int print(int x)

const int n=510,q=6e4+10;;

int n,test,cnt,x,t[n][n];

struct reca[n*n];

struct nodeq[q],l[q],r[q];

bool cmp(node x,node y)

int count(int a,int b,int c,int d)

void solve(int l,int r,int ql,int qr)

int mid=(l+r)/2;

for(re i=l;i<=mid;++i) ins(a[i].x,a[i].y,1);

int cnt1=0,cnt2=0;

for(re d,i=ql;i<=qr;++i)

for(re i=l;i<=mid ;++i) ins(a[i].x,a[i].y,-1);

for(re i=1;i<=cnt1;++i) q[ql+i-1]=l[i];

for(re i=1;i<=cnt2;++i) q[ql+cnt1-1+i]=r[i];

solve(l,mid,ql,ql+cnt1-1);

solve(mid+1,r,ql+cnt1,qr);

}signed main();

for(re i=1;i<=test;++i)read(q[i].a),read(q[i].b),read(q[i].c),read(q[i].d),read(q[i].k),q[i].num=i;

sort(a+1,a+cnt+1,cmp1),solve(1,cnt,1,test),sort(q+1,q+test+1,cmp);

for (re i=1;i<=test;++i) print(q[i].ans),puts("");

}

樹狀陣列1 樹狀陣列入門

仔細看一下,發現tree的每乙個節點的高度並不是隨意的,而是由它轉成二進位制之後末尾連續零的數量決定的,連續零的數量加1,就是高度,例如 3 11 零的數量為0,加1等於1,所以它的高度就是1 6 110 零的數量為1,加1等於2,所以它的高度就是2 8 1000 零的數量為3,加1等於4,所以它的...

樹狀陣列板題

洛谷p3374 樹狀陣列2 如題,已知乙個數列,你需要進行下面兩種操作 將某乙個數加上 求出某區間每乙個數的和 第一行包含兩個正整數 n,mn,mn,m,分別表示該數列數字的個數和操作的總個數。第二行包含 nnn 個用空格分隔的整數,其中第 iii 個數字表示數列第 iii 項的初始值。接下來 mm...

模板 樹狀陣列1

這篇是樹狀陣列模板1 主要內容有 1.將某數加上x 2.求某區間和 也就是說支援單點修改 關於樹狀陣列的部落格講解 見基本介紹 include include include using namespace std define in read typedef long long ll const ...