您的位置:首页 > 其它

HDU 4417 Super Mario (树状数组+离线处理)(划分树+二分答案)

2013-09-05 18:23 519 查看
题意: 给定1--n区间,有q个询问,询问l,r,k表示区间[l,r]小于等于k的数的个数

思路: 可以用划分树(求区间第k大值)变形一下,来求小于等于k的个数,但是此题直接离线处理询问高效的多。

首先将1--n区间的值记录位置,从小到大排序,每个询问按照k值从小到大排序,然后从小到大开始,根据查询的H,将满足条件的的点插入,计数+1,然后就是求区间和。

#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100001
using namespace std;

inline void RD(int &ret) {
char c;
do {
c = getchar();
} while(c < '0' || c > '9') ;
ret = c - '0';
while((c=getchar()) >= '0' && c <= '9')
ret = ret * 10 + ( c - '0' );
}
void OT(int a) {
if(a >= 10)OT(a / 10);
putchar(a % 10 + '0');
}

int n,q;
struct node {
int v,id;
} a[MAX];
int c[MAX];

struct QES {
int l,r,h,id;
} qes[MAX];
int ans[MAX];

bool cmp(const node &a, const node &b) {
return a.v < b.v;
}

bool cmp2(const QES &a, const QES &b) {
return a.h < b.h;
}

int lowbit(int x) {
return x & (-x);
}

void update(int x,int va) {
while(x <= n) {
c[x] += va;
x += lowbit(x);
}
}

int query(int x) {
int ans = 0;
while(x > 0) {
ans += c[x];
x -= lowbit(x);
}
return ans;
}
int main() {
int T;
cin >> T;
int ca = 1;
while(T--) {
RD(n); RD(q);
memset(c,0,sizeof(c));
for(int i=1; i<=n; i++) {
RD(a[i].v);
a[i].id = i;
}
sort(a+1,a+1+n,cmp);
for(int i=0; i<q; i++) RD(qes[i].l),RD(qes[i].r),RD(qes[i].h),qes[i].id = i;
sort(qes,qes+q,cmp2);
int order = 1;
for(int i=0; i<q; i++) {
while(a[order].v <= qes[i].h && order <= n) {
update(a[order].id,1);
order ++;
}
ans[qes[i].id] = query(qes[i].r+1) - query(qes[i].l);
}
printf("Case %d:\n",ca++);
for(int i=0; i<q; i++)OT(ans[i]),puts("");
}
return 0;
}


划分树:

要求的是小于等于k的个数,可以二分该个数mid,找到的也是第mid小的值,与k大小比较,直到得到答案

#include <iostream>
#include <algorithm>
#include <cmath>
#include<functional>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define L(x) x << 1
#define R(x) x << 1 | 1
using namespace std;

struct Seg_Tree {
int l,r,mid;
} tr[MAX*4];
int sorted[MAX];
int lef[20][MAX];
int val[20][MAX];

void build(int l,int r,int step,int x) {
tr[x].l = l;
tr[x].r = r;
tr[x].mid = (l + r) >> 1;
if(tr[x].l == tr[x].r) return ;
int mid = tr[x].mid;
int lsame = mid - l + 1;//lsame表示和val_mid相等且分到左边的
for(int i = l ; i <= r ; i ++) {
if(val[step][i] < sorted[mid]) {
lsame --;//先假设左边的数(mid - l + 1)个都等于val_mid,然后把实际上小于val_mid的减去
}
}
int lpos = l;
int rpos = mid + 1;
int same = 0;
for(int i = l ; i <= r ; i ++) {
if(i == l) {
lef[step][i] = 0;//lef[i]表示[ tr[x].l , i ]区域里有多少个数分到左边
} else {
lef[step][i] = lef[step][i-1];
}
if(val[step][i] < sorted[mid]) {
lef[step][i] ++;
val[step + 1][lpos++] = val[step][i];
} else if(val[step][i] > sorted[mid]) {
val[step+1][rpos++] = val[step][i];
} else {
if(same < lsame) {//有lsame的数是分到左边的
same ++;
lef[step][i] ++;
val[step+1][lpos++] = val[step][i];
} else {
val[step+1][rpos++] = val[step][i];
}
}
}
build(l,mid,step+1,L(x));
build(mid+1,r,step+1,R(x));
}

int query(int l,int r,int k,int step,int x) {
if(l == r) {
return val[step][l];
}
int s;//s表示[l , r]有多少个分到左边
int ss;//ss表示 [tr[x].l , l-1 ]有多少个分到左边
if(l == tr[x].l) {
s = lef[step][r];
ss = 0;
} else {
s = lef[step][r] - lef[step][l-1];
ss = lef[step][l-1];
}
if(s >= k) {//有多于k个分到左边,显然去左儿子区间找第k个
int newl = tr[x].l + ss;
int newr = tr[x].l + ss + s - 1;//计算出新的映射区间
return query(newl,newr,k,step+1,L(x));
} else {
int mid = tr[x].mid;
int bb = l - tr[x].l - ss;//bb表示 [tr[x].l , l-1 ]有多少个分到右边
int b = r - l + 1 - s;//b表示 [l , r]有多少个分到右边
int newl = mid + bb + 1;
int newr = mid + bb + b;
return query(newl,newr,k-s,step+1,R(x));
}
}

int n,m;

int main() {
int T;
cin >> T;
int l,r,k;
int ca = 1;
while(T--) {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&val[0][i]);
sorted[i] = val[0][i];
}
sort(sorted+1,sorted+1+n);
build(1,n,0,1);
printf("Case %d:\n",ca++);
for(int i=0; i<m; i++) {
scanf("%d%d%d",&l,&r,&k);
l ++;
r ++;
int ll = 1;
int rr = r - l + 1;
int mid,maxx = 0;
while(ll <= rr) {
mid = (ll + rr) >> 1;
if(query(l,r,mid,0,1) <= k) {
maxx = max(maxx,mid);
ll = mid + 1;
} else rr = mid - 1;
}
printf("%d\n",maxx);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: