1. 程式人生 > 其它 >P1527 [國家集訓隊]矩陣乘法 題解

P1527 [國家集訓隊]矩陣乘法 題解

題面

\(q\) 次查詢矩形第 \(k\) 小。考慮使用整體二分,二分一個值 \(mid\),矩形中小於等於 \(mid\) 的數設為 \(1\),大於 \(mid\) 的數設為 \(0\),統計每個詢問的矩形中的和,若大於等於 \(k\),表示答案應該在 \([L,mid]\) 範圍內,放到左邊繼續遞迴,否則放到右邊。

考慮一下整體二分的複雜度。整體二分需要處理的次數很多,但是可以按層考慮,每一層每一個詢問或修改只會被操作一次,因為只會出現在一個區間裡。所以總的複雜度就是 \(O(\)詢問層數 \(\log n\times\) 操作一個詢問或修改的複雜度 \()\),所以本題的時間複雜度為 \(O(n\log n\log v)\)

這道題目還應當注意二維樹狀陣列的寫法和求矩形內數的和的時候的式子。

點選檢視程式碼
#include<iostream>
#include<cstdio>
using namespace std;
inline int min(const int &a,const int &b){return a<b?a:b;}
inline int max(const int &a,const int &b){return a>b?a:b;}
const int N=4e5+13,M=500+13;
struct Node{int x,y,x2,y2,k,op;}q[N],lq[N],rq[N];
int n,m,tot,ans[N];
namespace BIT{
	int t[M][M];
	#define lowbit(x) (x&-x)
	inline void add(int x,int y,int k){
		for(int i=x;i<=n;i+=lowbit(i))
			for(int j=y;j<=n;j+=lowbit(j))
				t[i][j]+=k;
	}
	inline int sum(int x,int y){
		int res=0;
		for(int i=x;i;i-=lowbit(i))
			for(int j=y;j;j-=lowbit(j))
				res+=t[i][j];
		return res;
	}
}using BIT::add;using BIT::sum;
void solve(int L,int R,int l,int r){
	if(l>r) return;
	if(L==R){
		for(int i=l;i<=r;++i)
			if(q[i].op) ans[q[i].op]=L;
		return;
	}
	int mid=(L+R)>>1,lt=0,rt=0;
	for(int i=l;i<=r;++i){
		if(!q[i].op){
			if(q[i].k<=mid) add(q[i].x,q[i].y,1),lq[++lt]=q[i];
			else rq[++rt]=q[i];
		}
		else{
			int res=sum(q[i].x2,q[i].y2)-sum(q[i].x-1,q[i].y2)-sum(q[i].x2,q[i].y-1)+sum(q[i].x-1,q[i].y-1);
			if(res>=q[i].k) lq[++lt]=q[i];
			else q[i].k-=res,rq[++rt]=q[i];
		}
	}
	for(int i=l;i<=r;++i)
		if(!q[i].op&&q[i].k<=mid) add(q[i].x,q[i].y,-1);
	for(int i=1;i<=lt;++i) q[l+i-1]=lq[i];
	for(int i=1;i<=rt;++i) q[l+lt+i-1]=rq[i];
	solve(L,mid,l,l+lt-1);
	solve(mid+1,R,l+lt,r);
}
int main(){
	int ll=1e9,rr=0;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1,x;j<=n;++j){
			scanf("%d",&x);ll=min(ll,x),rr=max(rr,x);
			q[++tot]=(Node){i,j,0,0,x,0};
		}
	for(int i=1;i<=m;++i){
		++tot;
		scanf("%d%d%d%d%d",&q[tot].x,&q[tot].y,&q[tot].x2,&q[tot].y2,&q[tot].k);
		q[tot].op=i;
	}
	solve(ll,rr,1,tot);
	for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
	return 0;
}