hdu4417Super Mario【划分树+二分模板】

这个题暴露了自己很严重的基础知识问题==就是二分啊!因为自己知道当年(就是去年这会)二分学的不好,都一年了,还能怨人家课件吗??

于是乎在网上搜呀搜,搜到了点击打开链接 这篇博客,说的简直就是自己啊啊啊啊,还好给了模板,也非常好理解,顺便测试了一下

#include <iostream>
#include<cstdio>
using namespace std;
int num[10]={1,1,2,2,4,4,5,5,7,7};
/****************
answer:
1~YES_LEFT或者NO_RIGHT~is 1
1~YES_RIGHT或者NO_LEFT~is 2
2~YES_LEFT或者NO_RIGHT~is 3
2~YES_RIGHT或者NO_LEFT~is 4
3~YES_LEFT或者NO_RIGHT~is 5
3~YES_RIGHT或者NO_LEFT~is 4
4~YES_LEFT或者NO_RIGHT~is 5
4~YES_RIGHT或者NO_LEFT~is 6
5~YES_LEFT或者NO_RIGHT~is 7
5~YES_RIGHT或者NO_LEFT~is 8
6~YES_LEFT或者NO_RIGHT~is 9
6~YES_RIGHT或者NO_LEFT~is 8
7~YES_LEFT或者NO_RIGHT~is 9
7~YES_RIGHT或者NO_LEFT~is 10
*****************/
int bSearch1(int begin, int end, int e)///YES_LEFT或者NO_RIGHT
{
	int mid, left = begin, right = end;
	while(left <= right)
	{
		mid = (left + right) >> 1;
		if(num[mid] >= e) right = mid - 1;
		else left = mid + 1;
	}
	return left;
}
int bSearch2(int begin, int end, int e)///YES_RIGHT或者NO_LEFT
{
	int mid, left = begin, right = end;
	while(left <= right)
	{
		mid = (left + right) >> 1;
		if(num[mid] > e) right = mid - 1;
		else left = mid + 1;
	}
	return right;
}
int main()
{
    for(int i=1;i<=7;i++)
    {
        printf("%d~YES_LEFT或者NO_RIGHT~is %d\n",i,bSearch1(0,9,i)+1);
        printf("%d~YES_RIGHT或者NO_LEFT~is %d\n",i,bSearch2(0,9,i)+1);
    }

    return 0;
}

再说这个题,除了二分和划分树初级的,就没什么了==

/***************
hdu4417
2016.1.28
561MS 20228K 2603 B G++
390MS 20232K 2603B G++ new
***************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN=100110;
int tree[30][MAXN];//表示每层每个位置的值
int sorted[MAXN];//已经排序的数
int toleft[30][MAXN];//toleft[p][i]表示第i层从1到i有多少个数分入左边
int t,n, m;
void build(int l,int r,int dep)
{
    if(l==r)return;
    int mid=(l+r)>>1;
    int same=mid-l+1;//表示等于中间值而且被分入左边的个数
    for(int i=l;i<=r;i++)
      if(tree[dep][i]<sorted[mid])
         same--;
    int lpos=l;
    int rpos=mid+1;
    for(int i=l;i<=r;i++)
    {
        if(tree[dep][i]<sorted[mid])//比中间的数小,分入左边
             tree[dep+1][lpos++]=tree[dep][i];
        else if(tree[dep][i]==sorted[mid]&&same>0)
        {
            tree[dep+1][lpos++]=tree[dep][i];
            same--;
        }
        else  //比中间值大分入右边
            tree[dep+1][rpos++]=tree[dep][i];
        toleft[dep][i]=toleft[dep][l-1]+lpos-l;//从1到i放左边的个数

    }
    build(l,mid,dep+1);
    build(mid+1,r,dep+1);

}


//查询区间第k大的数,[L,R]是大区间,[l,r]是要查询的小区间
int query(int L,int R,int l,int r,int dep,int k)
{
    if(l==r)return tree[dep][l];
    int mid=(L+R)>>1;
    int cnt=toleft[dep][r]-toleft[dep][l-1];//[l,r]中位于左边的个数
    if(cnt>=k)
    {
        //L+要查询的区间前被放在左边的个数
        int newl=L+toleft[dep][l-1]-toleft[dep][L-1];
        //左端点加上查询区间会被放在左边的个数
        int newr=newl+cnt-1;
        return query(L,mid,newl,newr,dep+1,k);
    }
    else
    {
         int newr=r+toleft[dep][R]-toleft[dep][r];
         int newl=newr-(r-l-cnt);
         return query(mid+1,R,newl,newr,dep+1,k-cnt);
    }
}
int half(int a,int b,int x)
{
    int l=1,r=b-a+1,mid,ans=0;
    while(l<=r)
    {
        mid=(l+r)/2;
        int tmp=query(0,n-1,a,b,0,mid);
        if(tmp<=x) {ans=mid;l=mid+1;}
        else r=mid-1;
    }
    //return ans;
    return l;//new
}
int main()
{
   // freopen("cin.txt","r",stdin);
    int cnt=1;
    scanf("%d",&t);
    while(t--)
    {
        printf("Case %d:\n",cnt++);
        memset(tree,0,sizeof(tree));
        scanf("%d%d", &n,&m);
        for (int i=0; i<n; i++) {
            scanf("%d", &sorted[i]);
            tree[0][i] = sorted[i];
        }
        sort(sorted, sorted+n);
        build(0, n-1, 0);
        int a, b, k;
        while (m--) {
            scanf("%d%d%d", &a, &b, &k);
            //a++;b++;
            printf("%d\n",half(a,b,k)-1);
        }
    }
    return 0;
}


你可能感兴趣的:(数据结构)