[国家集训队] 矩阵乘法
题目描述
给你一个 \(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;
}
这个整体二分是两种写法,一种是把初值当作修改,一种是不当作,我这个是后面这种。
前面那种也可以写。如果这个题目加个修改的话,前面那种可以直接不用大改直接做
然后是二维树状数组熟悉了一下。
没什么了,只是例题而已。
写的还是不够熟练,变量名会写串。。