C. Store---(二分+线段树)

Store

time limit per test 1.0 s
memory limit per test 256 MB
题目链接http://whu2019.contest.codeforces.com/group/YyBKO8xFiH/contest/102167/problem/C

C. Store---(二分+线段树)_第1张图片
C. Store---(二分+线段树)_第2张图片


题目大意:lc是一个商店的店主,他现在想要确定已经卖出货物的比给定值K大的最小值。给你k的最大值n,和q次操作,M x,y,表示第x天卖出数量为y的商品,D x,y,表示询问在x天以前(含x)有没有卖出的商品数量大于等于y,如果有输出大于等于y的最小值,否则输出-1。

一看到题目就基本上想得到线段树,但我们的线段树要以什么为基础建立呢?仔细想一下也无非就两种情况,按照x(天数),按照y(数量)。但x的值可达到1e9,所以不做考虑。那么也就是说我们线段树的1到n的序号就是y的有序排列,要找比y大的,我们只需要在y到n的区间查找就ok了。

接下来就是解决天数小于x。之前我们已经建好了一颗空树,那么我们就应该在里面存些数据,不用想也知道要存x,但我们的底层存了x,他们的上层呢?由于需要天数小于x的,那么我们就存x的最小值。于是就变成了区间查找最小值了。

下一个问题就是我们可以用线段树查找在数量y-n之间是否有满足条件的,但我们无法确定到底是哪个单点满足,就更别说要寻找到y-n的最小值满足了。那么把这个问题单独提出来我们就应该很容易发现可以用二分答案。我们二分y-n,对于每一次的二分,我们对于区间y-mid询问是否满足条件,如果满足,我们就压缩n,否则y=mid。然后这道题就结束了。。。。

以下是详细代码:

#include 
#include 
#include 
const int mac=2e5+10;
const int inf=1e9+500;
using namespace std;
struct node
{
	int l,r,mi;
}tree[mac*4];
int mid,mark=0,ans=inf,p=inf,r,k,lr;
void build(int now,int l,int r);
void change_pot(int now);
void ask(int now);
int main()
{
	int n,q;
	scanf ("%d%d",&n,&q);
	build(1,1,mac);
	for (int i=1; i<=q; i++){
		char ch=getchar();
		while (ch!='D' && ch!='M') ch=getchar();
		if (ch=='M'){
			scanf ("%d%d",&k,&r);
			change_pot(1);
		}
		else if (ch=='D'){
			scanf ("%d%d",&k,&lr);
			ans=inf;
			int ll=lr,rr=mac;
			for (int j=1; j<=20; j++){
				p=inf;
				mid=(ll+rr)>>1;//以mid为右边界,k为左边界 
				mark=0;
				ask(1);
				if (mark){
					ans=mid;rr=mid;
				}
				else ll=mid;
			}
			if (ans==inf) printf ("-1\n");
			else printf ("%d\n",ans);
		}
	}
	return 0;
}
void build(int now,int l,int r)
{
	tree[now].r=r,tree[now].l=l;
    if (l==r){
        tree[now].mi=inf;
        return;
    }
    int mid1=(l+r)>>1;
    build (now*2,l,mid1);
    build (now*2+1,mid1+1,r);
    tree[now].mi=inf;
}
void change_pot(int now)
{
	if (tree[now].l==tree[now].r){
        tree[now].mi=k;
        return;
    }
    int mid1=(tree[now].l+tree[now].r)>>1;
    if (r<=mid1) change_pot(now*2);
    else change_pot(now*2+1);
    tree[now].mi=min(tree[now*2+1].mi,tree[now*2].mi);
}
void ask(int now)
{
	if (mark) return;
	if (tree[now].l>=lr && tree[now].r<=mid){
        p=min(p,tree[now].mi); 
        if (p<=k) {
			mark=1;
		}
        return;
    }
    int mid1=(tree[now].l+tree[now].r)>>1;
    if (lr<=mid1) ask(now*2);
    if (mid>mid1) ask(now*2+1);
}

你可能感兴趣的:(#,线段树&树状数组,#,二分&三分,#,2019.4武大校赛)