[国家集训队]矩阵乘法

题链:https://www.luogu.org/problemnew/show/P1527

 

感觉树套树可以,但是空间不够

没有修改,询问的类型相同,又可以离线,推测整体二分

 

试试看,发现是个二维整体二分

时间复杂度nlg(n)^3

 

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

int read()
{
	int ret=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9')
	{
		ret=(ret<<1)+(ret<<3)+ch-'0';
		ch=getchar();
	}
	return ret;
}

void write(int x)
{
	if((x/10)!=0) write(x/10);
	putchar(x%10+48);
}

const int N=505,M=300005;
int n,m,ans[M],c[N][N],bb[M];
struct A{int x1,y1,x2,y2,k,id; }q[M],q1[M],q2[M];
struct B{int v,x,y; }b[M];

inline void add(int x,int y,int k)
{
	for(int i=x;i<=n;i+=i&-i)
		for(int j=y;j<=n;j+=j&-j)
			c[i][j]+=k;
}

inline int sum(int x,int y)
{
	int ret=0;
	for(int i=x;i;i-=i&-i)
		for(int j=y;j;j-=j&-j)
			ret+=c[i][j];
	return ret;
}

int find1(int x)
{
	int l=1,r=n*n,ans=0;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(b[mid].v>=x) 
		{
			ans=mid; r=mid-1;
		}
			else l=mid+1;
	}
	return ans;
}

int find2(int x)
{
	int l=1,r=n*n,ans=1;
	while(l<=r)
	{
		int mid=l+r>>1;
		if(b[mid].v<=x)
		{
			ans=mid; l=mid+1;
		}
			else r=mid-1;
	}
	return ans;
}

void solve(int l,int r,int L,int R,int loc)
{
	if(L>R) return;
	if(l==r)
	{
		for(int i=L;i<=R;i++) 
			ans[q[i].id]=l;
		return;	
	}
	int mid=l+r>>1;
	if(loc!=0)
	{
		if(loc>mid) 
		{
			int t1=find1(mid+1),t2=find2(loc);
			for(int i=t1;i<=t2;i++)
				add(b[i].x,b[i].y,-1);
		}else
		if(loc<mid)
		{
			int t1=find1(loc+1),t2=find2(mid);
			for(int i=t1;i<=t2;i++)
				add(b[i].x,b[i].y,1);
		}
	}else
	{
		int t1=find2(mid);
		for(int i=1;i<=t1;i++)
			add(b[i].x,b[i].y,1);
	}
	int j=0,k=0;
	for(int i=L;i<=R;i++)
	{
		if(sum(q[i].x2,q[i].y2)-sum(q[i].x1-1,q[i].y2)-sum(q[i].x2,q[i].y1-1)+sum(q[i].x1-1,q[i].y1-1)>=q[i].k)
			q1[++j]=q[i];
		else
			q2[++k]=q[i];
	}for(int i=L;i<=L+j-1;i++)
		q[i]=q1[i-L+1];
	for(int i=L+j;i<=R;i++)
		q[i]=q2[i-L-j+1];
	solve(l,mid,L,L+j-1,mid);
	solve(mid+1,r,L+j,R,mid);
	if(loc!=0)
	{
		if(loc>mid) 
		{
			int t1=find1(mid+1),t2=find2(loc);
			for(int i=t1;i<=t2;i++)
				add(b[i].x,b[i].y,1);
		}else
		if(loc<mid)
		{
			int t1=find1(loc+1),t2=find2(mid);
			for(int i=t1;i<=t2;i++)
				add(b[i].x,b[i].y,-1);
		}
	}else
	{
		int t1=find2(mid);
		for(int i=1;i<=t1;i++)
			add(b[i].x,b[i].y,-1);
	}
}

bool cmp(B i,B j) { return i.v<j.v; }

int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			int t=read();
			b[(i-1)*n+j]=(B){t,i,j};
		}
	sort(b+1,b+n*n+1,cmp);
	int  c=1;
	for(int i=1;i<=n*n;i++)
	{
		bb[c]=b[i].v;
		b[i].v=c;
		if(i!=n*n&&b[i].v!=b[i+1].v) 
			c++;
	}
	for(int i=1;i<=m;i++)
	{
		q[i].x1=read(),q[i].y1=read();
		q[i].x2=read(),q[i].y2=read();
		q[i].k=read(),q[i].id=i;
	}
	solve(1,c,1,m,0);
	for(int i=1;i<=m;i++)
		write(bb[ans[i]]),puts("");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值