【題解】[JSOI2009]計數問題
阿新 • • 發佈:2021-06-28
\(\text{Solution:}\)
開始有一種暴力的做法:對每一行維護 \(100\) 個樹狀陣列對應 \(100\) 個顏色。查詢列舉行來查詢。
複雜度:\(O(m\cdot \log n\cdot q)\) 過不去的樣子。
考慮用二維樹狀陣列,直接維護二維矩陣。修改與查詢的複雜度都做到了 \(O(\log n\cdot \log m).\)
於是最終複雜度可以做到 \(O(q\log n\log m).\)
注意樹狀陣列實現上的細節:第二重迴圈要保證每次都是從 \(y\) 開始,所以要重新複製一個變數來做。而且兩個維度的最大值也是不一樣的,上界不同修改的時候要注意。
這題的樹狀陣列顯然沒有 上帝造題的七分鐘 那個樹狀陣列難。這題算是入門版的二維樹狀陣列。
維護矩陣同時維護一個顏色即可
#include<bits/stdc++.h> using namespace std; int tr[301][301][101],n,m,a[301][301],q; inline int lowbit(int x){return x&(-x);} void change(int x,int y,int c,int v){ for(;x<=n;x+=lowbit(x)) for(int i=y;i<=m;i+=lowbit(i)) tr[x][i][c]+=v; } int query(int x,int y,int c){ int res=0; for(;x;x-=lowbit(x)) for(int i=y;i;i-=lowbit(i)) res+=tr[x][i][c]; return res; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ scanf("%d",&a[i][j]); change(i,j,a[i][j],1); } scanf("%d",&q); for(;q;q--){ int opt; scanf("%d",&opt); if(opt==1){ int x,y,c; scanf("%d%d%d",&x,&y,&c); change(x,y,a[x][y],-1); a[x][y]=c; change(x,y,a[x][y],1); } else{ int ax,bx,ay,by,c; scanf("%d%d%d%d%d",&ax,&bx,&ay,&by,&c); int ans=query(bx,by,c)-query(ax-1,by,c)-query(bx,ay-1,c)+query(ax-1,ay-1,c); printf("%d\n",ans); } } return 0; }