MnZn求助分块套二分套二分的边界
查看原帖
MnZn求助分块套二分套二分的边界
430409
Coros_Trusds楼主2022/1/25 00:01

问题出在第一个二分的边界和第一个二分套的二分的边界上:

a 数组是原数组,b 数组是排序后第 j 块的数字。

代码:

#define int long long

const int ma=1e5+5;

int a[ma],b[ma],bel[ma],Left[ma],Right[ma];

int n,m;

inline void updata(int x,int k)
{
	int pos=lower_bound(b+Left[bel[x]],b+Right[bel[x]]+1,a[x])-b;

	a[x]=k,b[pos]=k;

	sort(b+Left[bel[x]],b+Right[bel[x]]+1);
}

inline int binary(int p,int k)
{
	int l=Left[p],r=Right[p];

	while(l<=r)
	{
		int mid=(l+r)>>1;

		if(b[mid]>k)
		{
			r=mid;
		}

		else
		{
			l=mid+1;
		}
	}

	return l;
}

inline int cnt(int l,int r,int num)//num 在区间 [l,r] 内是否是第 k 小
{
	int tmp=0;

	if(bel[l]==bel[r])
	{
		for(register int i=l;i<=r;i++)
		{
			if(num>a[i])
			{
				tmp++;
			}
		}
	}

	else
	{
		for(register int i=l;i<=Right[bel[l]];i++)
		{
			if(num>a[i])
			{
				tmp++;
			}
		}

		for(register int i=Left[bel[r]];i<=r;i++)
		{
			if(num>a[i])
			{
				tmp++;
			}
		}

		for(register int i=bel[l]+1;i<bel[r];i++)
		{
			int now=binary(i,num);

			while(now>Left[i] && b[now-1]==b[now])//找到最前面的
			{
				now--;
			}

			tmp+=now-Left[i];
		}
	}

	return tmp;
}

inline int query(int le,int ri,int k)
{
	int l=1,r=1e9;

	while(l+1<r)//二分第 k 小的数 mid
	{
		int mid=(l+r)>>1;

		if(cnt(le,ri,mid)>=k)
		{
			l=mid;
		}

		else
		{
			r=mid;
		}
	}

	return l;
}

#undef int

int main(void)
{
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
#endif

	#define int long long

	n=read(),m=read();

	for(register int i=1;i<=n;i++)
	{
		a[i]=b[i]=read();
	}

	int len=sqrt(n),num=ceil((double)(n)/(double)(len));

	for(register int i=1;i<=num;i++)
	{
		Left[i]=(i-1)*len+1;

		Right[i]=i*len;
	}

	Right[num]=n;

	for(register int i=1;i<=num;i++)
	{
		for(register int j=Left[i];j<=Right[i];j++)
		{
			bel[j]=i;
		}
	}

	for(register int i=1;i<=num;i++)
	{
		sort(b+Left[i],b+Right[i]+1);
	}

	for(register int i=1;i<=num;i++)
	{
		printf("i = %lld,Left = %lld,Right = %lld\n",i,Left[i],Right[i]);
	}

	for(register int i=1;i<=m;i++)
	{
		char opt[3];

		scanf("%s",opt);

		if(opt[0]=='Q')
		{
			int l=read(),r=read(),k=read();

			printf("%lld\n",query(l,r,k));
		}

		else
		{
			int x=read(),k=read();

			updata(x,k);
		}
	}

	return 0;
}

第一个二分

正确的:

//区间 [le,ri] 找第 k 小
inline int query(int le,int ri,int k)
{
	int l=1,r=1e9;

	while(l+1<r)//二分第 k 小的数 mid
	{
		int mid=(l+r)>>1;

		if(cnt(le,ri,mid)>=k)//统计区间 [le,ri] 比 mid 小的数的个数。
		{
			l=mid;
		}

		else
		{
			r=mid;
		}
	}

	return l;
}

错误的:

//区间 [le,ri] 找第 k 小
inline int query(int le,int ri,int k)
{
	int l=1,r=1e9;

	while(l+1<r)//二分第 k 小的数 mid
	{
		int mid=(l+r)>>1;

		if(cnt(le,ri,mid)>=k)//统计区间 [le,ri] 比 mid 小的数的个数。
		{
			r=mid;
		}

		else
		{
			l=mid;
		}
	}

	return l;
}

inline int query(int le,int ri,int k) { int l=1,r=1e9;

while(l<r)//二分第 k 小的数 mid
{
	int mid=(l+r)>>1;

	if(cnt(le,ri,mid)>=k)
	{
		r=mid;
	}

	else
	{
		l=mid+1;
	}
}

return l;

}


第二个二分:

正确的:
```cpp
inline int binary(int p,int num)
{
	int l=Left[p],r=Right[p];

	while(l<=r)
	{
		int mid=(l+r)>>1;

		if(b[mid]>num)
		{
			r=mid-1;
		}

		else if(b[mid]<num)
		{
			l=mid+1;
		}

		else
		{
			l=mid;

			break;
		}
	}

	return l;
}

错误的:

//求 b 数组中区间 [Left[p],Right[p]] 中 k 的位置
inline int binary(int p,int k)
{
	int l=Left[p],r=Right[p];

	while(l<r)
	{
		int mid=(l+r)>>1;

		if(b[mid]>=k)
		{
			r=mid;
		}

		else
		{
			l=mid;
		}
	}

	return l;
}

问题:

欲将二分边界变为 while(l<r) 该如何写?

2022/1/25 00:01
加载中...