洛谷 P1527 [國家集訓隊]矩陣乘法 解題報告
阿新 • • 發佈:2018-11-01
P1527 [國家集訓隊]矩陣乘法
題目描述
給你一個\(N*N\)的矩陣,不用算矩陣乘法,但是每次詢問一個子矩形的第\(K\)小數。
輸入輸出格式
輸入格式:
第一行兩個數\(N,Q\),表示矩陣大小和詢問組數;
接下來\(N\)行\(N\)列一共\(N*N\)個數,表示這個矩陣;
再接下來\(Q\)行每行\(5\)個數描述一個詢問:\(x1,y1,x2,y2,k\)表示找到以\((x1,y1)\)為左上角、以\((x2,y2)\)為右下角的子矩形中的第\(K\)小數。
輸出格式:
對於每組詢問輸出第\(K\)小的數。
說明
矩陣中數字是\(10^9\)以內的非負整數;
\(20\%\)
\(40\%\)的資料:\(N<=300,Q<=10000\);
\(60\%\)的資料:\(N<=400,Q<=30000\);
\(100\%\)的資料:\(N<=500,Q<=60000\)。
整體二分真有趣。
直接把整體二分裡面的改成二維樹狀陣列就可以了
複雜度\(O((N^2+M)\log10^9\log N^2)\)
Code:
#include <cstdio> const int N=502; const int M=310010; const int inf=0x3f3f3f3f; struct node{int op,a,b,c,d,k;}q[M],ql[M],qr[M]; int s[N][N],n,m,Q,ans[M]; void add(int x,int y,int d) { for(int i=x;i<=n;i+=i&-i) for(int j=y;j<=n;j+=j&-j) s[i][j]+=d; } int query(int x,int y) { int sum=0; for(int i=x;i;i-=i&-i) for(int j=y;j;j-=j&-j) sum+=s[i][j]; return sum; } #define rep(i,a,b) for(int i=a;i<=b;i++) void divide(int l,int r,int s,int t) { if(s>t)return; if(l==r){rep(i,s,t)ans[q[i].op]=l;return;} int mid=l+r>>1,lp=0,rp=0; rep(i,s,t) { if(q[i].op) { int p=query(q[i].c,q[i].d)-query(q[i].c,q[i].b-1) -query(q[i].a-1,q[i].d)+query(q[i].a-1,q[i].b-1); if(q[i].k<=p) ql[++lp]=q[i]; else qr[++rp]=q[i],qr[rp].k-=p; } else { if(q[i].k<=mid) add(q[i].a,q[i].b,1),ql[++lp]=q[i]; else qr[++rp]=q[i]; } } rep(i,s,t) if(!q[i].op&&q[i].k<=mid) add(q[i].a,q[i].b,-1); rep(i,s,s+lp-1) q[i]=ql[i+1-s]; rep(i,s+lp,t) q[i]=qr[i+1-s-lp]; divide(l,mid,s,s+lp-1),divide(mid+1,r,s+lp,t); } int main() { scanf("%d%d",&n,&Q); rep(i,1,n) rep(j,1,n) ++m,scanf("%d",&q[m].k),q[m].a=i,q[m].b=j; rep(i,1,Q) ++m,scanf("%d%d%d%d%d",&q[m].a,&q[m].b,&q[m].c,&q[m].d,&q[m].k),q[m].op=i; divide(0,inf,1,m); rep(i,1,Q) printf("%d\n",ans[i]); return 0; }
2018.11.1