[国家集训队] 矩阵乘法 整体二分

[国家集训队] 矩阵乘法

题目描述

给你一个 \(n \times n\) 的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第 \(k\) 小数。

输入格式

第一行有两个整数,分别表示矩阵大小 \(n\) 和询问组数 \(q\)

\(2\) 到第 \((n + 1)\) 行,每行 \(n\) 个整数,表示这个矩阵。第 \((i + 1)\) 行的第 \(j\) 个数表示矩阵第 \(i\) 行第 \(j\) 列的数 \(a_{i, j}\)

接下来 \(q\) 行,每行五个整数 \(x_1, y_1, x_2, y_2, k\),表示一组询问,要求找到以 \((x_1, y_1)\) 为左上角,\((x_2, y_2)\) 为右下角的子矩形中的第 \(k\) 小数。

输出格式

对于每组询问,输出一行一个整数表示答案。

样例 #1

样例输入 #1

2 2
2 1
3 4
1 2 1 2 1
1 1 2 2 3

样例输出 #1

1
3

提示

数据规模与约定

  • 对于 \(20\%\) 的数据,保证 \(n \leq 100\)\(q \leq 10^3\)
  • 对于 \(40\%\) 的数据,保证 \(n \leq 300\)\(q \leq 10^4\)
  • 对于 \(60\%\) 的数据,保证 \(n \leq 400\)\(q \leq 3 \times 10^4\)
  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 500\)\(1 \leq q \leq 6 \times 10^4\)\(0 \leq a_{i, j} \leq 10^9\)

把整体二分例题朴实无华的扩展到了二维
(还可以上树,直接dfs序就好了)
这个题目能用整体二分的原因我觉得也是差不多的
单个询问能够用二分答案解决,所以能用。
最近写的整体二分的题目的代码我感觉我都调了好久,是因为不专注吗?
不知道

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
int tr[501][501],n,m,q_,ans[60001];//¸÷È¡ËùÐèµÄ¸Ð¾õ°É Õâ¸öÆ«Ðò 
inline int lowbit(int x)
{
	return x&(-x);
}
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))
		{
			tr[i][j]+=k;
		}
	}
}
int ask(int x,int y)
{
	int ans=0;
	for(int i=x;i>0;i-=lowbit(i))
		for(int j=y;j>0;j-=lowbit(j))
		{
			ans+=tr[i][j];
		}
	return ans;
}
int ask_(int x1,int y1,int x2,int y2)
{
	return ask(x2,y2)-ask(x1-1,y2)-ask(x2,y1-1)+ask(x1-1,y1-1);
}
struct Matrix
{
	int x,y,v;
}a[250001];
bool mycmp(Matrix a,Matrix b)
{
	if(a.v==b.v&&a.x==b.x)return a.y<b.y;
	if(a.v==b.v)return a.x<b.x;
	return a.v<b.v;
}
struct query
{
	int id,x1,x2,y1,y2,k;
}q[60001],ql[60001],qr[60001];
void solve(int st,int ed,int l,int r)// st denote the start of interval of Martrix,ed the same 
{
	if(l>r)return;
	if(st==ed)
	{
		for(int i=l;i<=r;i++)
		{
			ans[q[i].id]=a[st].v;
		}
		return;
	}
	int mid=(st+ed)>>1;
	for(int i=st;i<=mid;i++)
	{
		add(a[i].x,a[i].y,1);
	}
	int tl=0,tr=0;
	for(int i=l;i<=r;i++)
	{
		int Get=ask_(q[i].x1,q[i].y1,q[i].x2,q[i].y2);
		if(Get<q[i].k)
		{
			qr[++tr]=q[i];
			qr[tr].k-=Get;
		}
		else
		{
			ql[++tl]=q[i];
		}
	}
	for(int i=st;i<=mid;i++)
	{
		add(a[i].x,a[i].y,-1);
	}
	for(int i=l;i<=l+tl-1;i++)
	{
		q[i]=ql[i-l+1];
	}
	for(int i=l+tl;i<=r;i++)
	{
		q[i]=qr[i-l-tl+1];
	}
	solve(st,mid,l,l+tl-1);
	solve(mid+1,ed,l+tl,r);
}
int main()
{
//	freopen("1.in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),q_=read();int tot=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			a[++tot].x=i;
			a[tot].y=j;
			a[tot].v=read();
		}
	}
	sort(a+1,a+1+tot,mycmp);
	for(int i=1;i<=q_;i++)
	{
		q[i].x1=read(),q[i].y1=read(),q[i].x2=read(),q[i].y2=read();
		q[i].id=i;q[i].k=read();
	}
	solve(1,tot,1,q_);
	for(int i=1;i<=q_;i++)
	{
		cout<<ans[i]<<endl;
	}
	return 0;
}

这个整体二分是两种写法,一种是把初值当作修改,一种是不当作,我这个是后面这种。
前面那种也可以写。如果这个题目加个修改的话,前面那种可以直接不用大改直接做
然后是二维树状数组熟悉了一下。
没什么了,只是例题而已。
写的还是不够熟练,变量名会写串。。

posted @ 2024-01-17 14:19  HL_ZZP  阅读(12)  评论(0编辑  收藏  举报