您的位置:首页 > 其它

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

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

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

#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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: