[國家集訓隊]矩陣乘法 Solution

題意:給一個矩陣,每次詢問一個子矩陣裏面的第kk小的數。
如果它只有一個詢問,直接排個序就行,考慮如何用樹狀數組來操作。也就是樹狀數組如何查詢排名。
具體實現就是:每次二分一個值checkcheck,然後把小於該值的點全部加入二位樹狀數組裏,查這個矩形的時候直接判一下前綴和即可。
接下來考慮如何把如上方法轉移到多組詢問裏。顯然可以整體二分。
注意二分的是位置,返回排完序的元素裏面這個位置的值來判定即可,省去了離散化。
code:code:

/*
考慮如何使用整體二分求解。
二分這個位置,然後把小於這些數值的點染黑加入二位樹狀數組裏,根據前綴和判定答案大小,把答案小的和答案大的分開來處理即可。 
爲了保證時間複雜度的正確性,每次分成兩半遞歸時去掉所有貢獻,爲那些大於mid的值記一下上次的值 
*/
#include <bits/stdc++.h>
#define regi register int
int n,Q;
int ans[60101];
int id[60101];
int last[60101];
int c[511][511];
int tmpleft[60101];
int tmpright[60101];
int nn;
struct Matrix{
	int x;
	int y;
	int v;
}ma[500001];
struct Question{
	int lx;
	int ly;
	int rx;
	int ry;
	int k;
}q[60101];
inline int read(){
	int r=0,w=0,c;
	for(;!isdigit(c=getchar());r=c);
	for(w=c^48;isdigit(c=getchar());w=w*10+(c^48));
	return r^45?w:-w;
}
bool cmp(Matrix x,Matrix y){
	return x.v<y.v;
}
int add(int x,int y,int v){
	for(regi i=x;i<=n;i+=i&-i)
	  for(regi j=y;j<=n;j+=j&-j)
	    c[i][j]+=v;
}
int ask(int x,int y){
	int s=0;
	for(regi i=x;i>0;i-=i&-i)
	  for(regi j=y;j>0;j-=j&-j)
	    s+=c[i][j];
	return s;
}
int return_more(int lx,int ly,int rx,int ry){
  return ask(rx,ry)-ask(lx-1,ry)-ask(rx,ly-1)+ask(lx-1,ly-1);
} 
void solve(int l,int r,int ql,int qr){
	if(ql>qr)
	  return;
	if(l==r){
	  for(regi i=ql;i<=qr;++i)
	    ans[id[i]]=ma[l].v;
	  return;
	}
	int mid=l+r>>1;
	for(regi i=l;i<=mid;++i)
	  add(ma[i].x,ma[i].y,1);
	int tl=0,tr=0;
	for(regi i=ql;i<=qr;++i){
		int X=id[i];
	  regi S=last[X]+return_more(q[X].lx,q[X].ly,q[X].rx,q[X].ry);
	  if(S>=q[X].k)
	    tmpleft[++tl]=X;
	  else{
	  	tmpright[++tr]=X;
	  	last[X]=S;
	  }
	}
	for(regi i=1;i<=tl;++i)
	  id[ql+i-1]=tmpleft[i];
	for(regi i=1;i<=tr;++i)
	  id[ql+tl+i-1]=tmpright[i];
	for(regi i=l;i<=mid;++i)
    add(ma[i].x,ma[i].y,-1);
  solve(l,mid,ql,ql+tl-1);
  solve(mid+1,r,ql+tl,qr);
}
main(){
	n=read();
	Q=read();
	for(regi i=1;i<=n;++i)
	  for(regi j=1;j<=n;++j)
	    ma[++nn]={i,j,read()};
	std::sort(ma+1,ma+nn+1,cmp);
	for(regi i=1;i<=Q;++i){
		q[i].lx=read();
		q[i].ly=read();
		q[i].rx=read();
		q[i].ry=read();
		q[i].k=read();
	}
	for(regi i=1;i<=Q;++i)
	  id[i]=i;
	solve(1,nn,1,Q);
	for(regi i=1;i<=Q;++i)
	  printf("%d\n",ans[i]);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章