洛谷P1527[国家集训队]矩阵乘法(整体二分+树状数组)
整体二分经典题。对于一个$mid$,把所有小于$mid$的数染色,检查区间和即可。只是$O(qlog^3n)$需要卡常。二维树状数组是必须的,还有一个小技巧是:把所有数和对应的位置排序后,用一个cur记录一下现在树状数组的状态是1~cur染色过,每次移动cur,这样就省去了清空的复杂度。
#include<cstdio> #include<algorithm> using namespace std; char rB[1<<21],*rS,*rT,wB[(1<<21)+50]; int wp=-1; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;} inline void flush(){fwrite(wB,1,wp+1,stdout);wp=-1;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } short buf[15]; inline void wt(int x){ if(wp>(1<<21))flush(); short l=-1; while(x>9){ buf[++l]=x%10; x/=10; } wB[++wp]=x|48; while(l>=0)wB[++wp]=buf[l--]|48; wB[++wp]='\n'; } const int N=505; const int M=60005; int c[N][N],n,cur=0,s[M],b[N*N]; struct point{ int x,y,k; }a[N*N]; struct query{ int x1,y1,x2,y2,k,id; }Q[M],Q1[M],Q2[M]; inline bool cmp(point a,point b){return a.k<b.k;} inline void add(int x,int y,short k){ for(int i=x,j;i<=n;i+=i&-i) for(j=y;j<=n;j+=j&-j)c[i][j]+=k; } inline int sum(int x,int y){ int i,j,s=0; for(i=x;i;i^=i&-i) for(j=y;j;j^=j&-j)s+=c[i][j]; return s; } void solve(int L,int R,int l,int r){ if(l>r)return; if(L==R){ for(int i=l;i<=r;++i)s[Q[i].id]=b[L]; return; } int M=L+R>>1,i,cnt1=0,cnt2=0; while(a[cur].k<=M){ ++cur; add(a[cur].x,a[cur].y,1); } for(;a[cur].k>M;--cur)add(a[cur].x,a[cur].y,-1); for(i=l;i<=r;++i)if(sum(Q[i].x2,Q[i].y2)-sum(Q[i].x2,Q[i].y1-1)-sum(Q[i].x1-1,Q[i].y2)+sum(Q[i].x1-1,Q[i].y1-1)>=Q[i].k)Q1[cnt1++]=Q[i]; else Q2[cnt2++]=Q[i]; for(i=0;i<cnt1;++i)Q[l+i]=Q1[i]; for(i=0;i<cnt2;++i)Q[l+cnt1+i]=Q2[i]; solve(L,M,l,l+cnt1-1); solve(M+1,R,l+cnt1,r); } int main(){ int q,m,i,j; n=rd();q=rd(); for(i=1;i<=n;++i) for(j=1;j<=n;++j){a[(i-1)*n+j].x=i;a[(i-1)*n+j].y=j;b[(i-1)*n+j]=a[(i-1)*n+j].k=rd();} sort(b+1,b+n*n+1);m=unique(b+1,b+n*n+1)-b-1; for(i=1;i<=n*n;++i)a[i].k=lower_bound(b+1,b+m+1,a[i].k)-b; sort(a+1,a+n*n+1,cmp); for(i=0;i<q;++i){Q[i].x1=rd();Q[i].y1=rd();Q[i].x2=rd();Q[i].y2=rd();Q[i].k=rd();Q[i].id=i;} solve(1,m,0,q-1); for(i=0;i<q;++i)wt(s[i]); flush(); return 0; }