CF#307-E. GukiZ and GukiZiana-分块法/平方分桶

102 篇文章 0 订阅
4 篇文章 0 订阅

题意:

给出n,m(n范围是【1,5e5】、m范围是【1,5e4】)

表示原来有n个数

现在有m次操作  val范围是【1,1e9】

操作1: 参数 L,R,val 表示区间【L,R】加上val      

操作2: 参数val、表示在整个区间查找值val,找到相距最远的2个val,输出下标之差,找不到输出-1


思路:

对于操作1,可以用线段树解决,但是线段树不方便实现查询某个值。。

所以得用到一种 分块法/平方分桶法  其实思想和线段树类似

就是 先 分出N/ sqrt(N)个块,每个块存sqrt(n)个元素,并且每个块内部排好序(方便操作2的二分)

用一个大小为sqrt(N)的数组,来记录对整个块一起增加的val   【mark数组】

那么对于操作1  add val to【L,R】

我们只需先求出tl=L/len;  tr=R/len;   tl,tr表示L,R所位于的块,

那么显然如果L,R距离很远的话,中间的块,我们都可以直接 在mark【i】加上val,表示对整个块的每一个元素都加val

然后剩下的只有 与L,R相交的两个端点块了。

这个就直接暴力一遍,  把相交的块直接clear掉,再把属于【L,BL*LEN】的元素直接加上val,然后重新构建这个 被clear的块 

****注意 当tl==tr这个情况特殊处理一下就好了 

-------------------------------------------------------------------------------------------------------------------------------------

 

那么对于操作2  query(val)    

由于存在一个mark数组表示这个块整体加了多少量;

所以我们实际查询的应该是val=val-mark[i]

然后

我们直接 从第一个块 到最后一个块遍历

for (i=0;i<=N/sqrt(N);i++)

{

//在 第 i 块中二分查找 到第一个出现的val(原始下标最小),记为L

}

如果找不到 直接输出-1 ;continue;

如果找到了

for (i=N/sqrt(N);i>=0;i--)

{

//在 第 i 块中二分查找 到最靠右出现的val(原始下标最大),记为R

}

然后 R-L就是答案了

-------------------------------------------------------------------------------------------------------------------------------------

复杂度分析:

操作1:  

【复杂度A】【如果tl==tr】 也就是l,r在同一个块,那么整个块元素不超过sqrt(N), 所以复杂度是sqrt(N)+ sqrt(N)* log 【sqrt(N)】 、前者是遍历更新,后者是排序

【复杂度B】【其余情况】,要操作的块分为二部分,两端不完全包含的块,和中间被完全包含的块,前者复杂度就是tl==tr的一样啦,后者是O(1),最多有N/sqrt(N)块

所以最糟糕复杂度也就是  2*【复杂度A】+N/sqrt(N);

-------------------------------------------------------------------------所以操作也总的复杂度也就是sqrt(N)* log 【sqrt(N)】级别的

操作2:

直接遍历所有块,N/sqrt(n)个块,每次二分查找 log(sqrt(N));

-------------------------------------------------------------------------所以操作也总的复杂度也是sqrt(N)* log 【sqrt(N)】级别的


所以整体复杂度 就是 m*sqrt(N)* log 【sqrt(N)】级别的   

N规模是5e5,m是5e4

50000* 707* 9.46=334411000... 

x

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;  
__int64 n,m; 
struct node
{
	__int64 x,num;
	node(){}
	node(__int64 a,__int64 b)
	{
		x=a;
		num=b;
	}
	bool operator <( const node& b)const
	{
		if (x!=b.x)
			return x<b.x;
		else
			return num<b.num;
	}
		bool operator >( const node& b)const
	{
		if (x!=b.x)
			return x>b.x;
		else
			return num>b.num;
	}
};
__int64 tm[5*100000+50];
__int64 mark[1005];
vector<node> sb[1005];
__int64  init()
{
	__int64 i;
	__int64 cnt=-1;	  
	__int64 len=sqrt(n);
	for(i=0;i<n;i++)	 
	{ 
		if (i%len==0)		//0到len-1为第一块
			cnt++;
		sb[cnt].push_back(node(tm[i],i)); 
	}
	for (i=0;i<=cnt;i++)
		sort(sb[i].begin(),sb[i].end());
	return cnt; 
}

int main()
{
	__int64 op,l,r,val,i,j,k;
	__int64 cnt=0; 
	scanf("%I64d%I64d",&n,&m); 
	for (i=0;i<n;i++)
	{
		scanf("%I64d",&tm[i]);
	}
	cnt=init();		//块数 
	__int64 len=sqrt(n);  //块大小
	for (k=1;k<=m;k++)
	{
		scanf("%I64d",&op);
		if (op==1)
		{
			scanf("%I64d%I64d%I64d",&l,&r,&val);
			l--;r--;
			__int64 tl=l/len;	//【0,len-1】都得到tl=1
			__int64 tr=r/len;
			if (!val) continue;
			for (i=tl+1;i<=tr-1;i++) 
				mark[i]+=val;
			sb[tl].clear();
			sb[tr].clear();
			//两端相交区间
			if (tl==tr)
			{
				for (i=l;i<=r;i++)  
						tm[i]+=val;
				for (i=tl*len;i<tl*len+len;i++) 
					sb[tl].push_back(node(tm[i],i));
				sort(sb[tl].begin(),sb[tl].end());
			}
			else
			{
				for (i=l;i<tl*len+len;i++)  
					tm[i]+=val; 
				for (i=tr*len;i<=r;i++)  
					tm[i]+=val; 
				for (i=tl*len;i<tl*len+len;i++)
					sb[tl].push_back(node(tm[i],i));
				for (i=tr*len;i<tr*len+len;i++)
					sb[tr].push_back(node(tm[i],i));
				sort(sb[tl].begin(),sb[tl].end());
				sort(sb[tr].begin(),sb[tr].end()); 
			}
			
		}
		else	
			if (op==2)
		{
			scanf("%I64d",&val);
			l=-1;r=0;
			for (i=0;i<=cnt;i++)
			{
				__int64 tmp=val-mark[i];
				__int64 it=upper_bound(sb[i].begin(),sb[i].end(),node(tmp,-1))-sb[i].begin();
				//因为num也是比较的一部分,所以如果存在,则返回num最小的tmp
				if (it==len)	continue;	//找不到比(tmp,-1)大的,返回end
				if (sb[i][it].x!=tmp) continue;//找到比(tmp,-1)大的,但值不为tmp 
				l=sb[i][it].num;//num最小的tmp
				break;
			}
			if (l==-1)
			{
				printf("-1\n");
				continue;
			}
			for (i=cnt;i>=0;i--)
			{
				__int64 tmp=val-mark[i];
				__int64 it=upper_bound(sb[i].begin(),sb[i].end(),node(tmp,5e6))-sb[i].begin();
				if (it!=0) it--;
				if (sb[i][it].x!=tmp) continue;
				r=sb[i][it].num;
				break;
			}
			printf("%I64d\n",r-l);
		}
	}
	 
	
	return 0;
	
}



第二次写的代码:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
inline __int64 min(__int64 a,__int64 b)
{return a<b?a:b;}
struct node
{
	__int64 x,id;
	node(__int64 a=0,__int64 b=0)
	{x=a,id=b;}
};
bool sort_cmp(const node & a,const node & b)  
{ 
	if (a.x!=b.x)
		return a.x<b.x; 
	return a.id<b.id;	
}
bool cmp(const node & a,const node & b)  
{ 
	return a.x<b.x; 
}
struct sg
{	
	node xx[800];
	__int64 num; 
	__int64 val;
	sg()
	{num=val=0;}
};
sg tm[800];
__int64 aa[500005+50];
int main()
{  
	__int64 i,j;
	int q,n;
	cin>>n>>q;
 	for (i=0;i<n;i++)
 		scanf("%I64d",&aa[i]);
		__int64 block_len=sqrt(1.0*n);

	__int64 sg_num=n/block_len;
	if (n%block_len)
		sg_num++;
	
	for (i=0;i<n;i++)
	{
		__int64 what=i/block_len;
		tm[what].xx[++tm[what].num]=node(aa[i],i); 
	}
	for (i=0;i<sg_num;i++)
		sort(tm[i].xx+1,tm[i].xx+1+tm[i].num,sort_cmp);
	
	__int64 op,x,l,r,kk;
	for (kk=1;kk<=q;kk++)
	{
		scanf("%I64d",&op);
		if (op==1)
		{
			scanf("%I64d%I64d%I64d",&l,&r,&x);
			__int64 st=(l-1)/block_len;
			__int64 ed=(r-1)/block_len;
			if (!x)continue;
			if (st==ed)
			{
				for (i=l-1;i<=r-1;i++)
					aa[i]+=x;
				tm[st].num=0; 
				__int64 len= min(st*block_len+block_len,n);			//一定要限制右端点,感觉理论上不限制不会错,但是一直wa
				for (i=st*block_len;i<len;i++)
					tm[st].xx[++tm[st].num]=node(aa[i] ,i);
				sort(tm[st].xx+1,tm[st].xx+1+tm[st].num,sort_cmp);
			//	tm[st].val=0;											//val要么永久留在区间,要么加到aa中,不可加在node中
			}
			else
			{	
				__int64 len= st*block_len+block_len ;
				for (i=l-1;i<len;i++)
					aa[i]+=x;
				tm[st].num=0;
				for (i=st*block_len;i<len;i++)
					tm[st].xx[++tm[st].num]=node(aa[i] ,i);
				sort(tm[st].xx+1,tm[st].xx+1+tm[st].num,sort_cmp);
			//	tm[st].val=0;
				
				for (i=ed*block_len;i<r;i++)
					aa[i]+=x;
				tm[ed].num=0;
				  len=min( ed*block_len+block_len ,n);				//一定要限制右端点
				for (i=ed*block_len;i<len;i++)
					tm[ed].xx[++tm[ed].num]=node(aa[i] ,i);
				sort(tm[ed].xx+1,tm[ed].xx+1+tm[ed].num,sort_cmp);
			//	tm[ed].val=0;
				for (i=st+1;i<=ed-1;i++) 
					tm[i].val+=x; 
			}  
		}
		else
		{
			__int64 y;
			scanf("%I64d",&y);
			__int64 ll=-1;
			__int64 rr=-1;
			for (i=0;i<sg_num;i++)
			{ 
					__int64 it=lower_bound(tm[i].xx+1,tm[i].xx+1+tm[i].num,y-tm[i].val,cmp)-tm[i].xx;
					if (it==tm[i].num+1) continue; //不存在该元素
					else
					{
						if (tm[i].xx[it].x==y-tm[i].val) {ll=tm[i].xx[it].id;break;}
						else continue;
					} 
			}
			
			for (i=sg_num-1;i>=0;i--)
			{
				 	__int64 it=upper_bound(tm[i].xx+1,tm[i].xx+1+tm[i].num,y-tm[i].val,cmp)-tm[i].xx;
					it--;
					if (it==0) continue;  //不存在该元素y
					if (tm[i].xx[it].x==y-tm[i].val)   {rr=tm[i].xx[it].id;break;}
					else continue; 
			}
			if (ll==-1)
				printf("-1\n");
			else
				printf("%I64d\n",rr-ll);
			
		}
	}
	
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值