【SPOJ KQUERY】【HDU 4417】静态区间rank查询

SPOJ KQUERY:给一个数列,查询x在区间l,r上有多少数字>x

HDU4417:给一个数列,查询x在区间l,r上有多少数字<=x

离线树状数组,在线主席树,都可以做到logn复杂度的查询,不过主席树的常数超级大,SPOJ那题打死都是TLE,不能直视~

树状数组做法是先对所有询问按x从小到大排序,然后把原数列里<=x的数按下标加入树状数组,每次查询区间lr的区间和。

代码如下:

SPOJ KQUERY

int z[NN],a[NN],b[NN],lb;
int u[MM],v[MM],w[MM],id[MM],ans[MM];
int n,m;
void update(int x)
{
	while (x<=n)
	{
		z[x]++;
		x+=lowbit(x);
	}
}
int sum(int x)
{
	int ans=0;
	while (x>0)
	{
		ans+=z[x];
		x-=lowbit(x);
	}
	return ans;
}
int cmp(int i,int j)
{
	return w[i]<w[j];
}
int cmp2(int i,int j)
{
	return a[i]<a[j];
}
int main()
{
	n=fastget();
	for(int i=1;i<=n;i++)
		a[b[i]=i]=fastget();
	m=fastget();
	for(int i=1;i<=m;i++)
	{
		u[i]=fastget();
		v[i]=fastget();
		w[i]=fastget();
		id[i]=i;
	}
	sort(id+1,id+1+m,cmp);
	sort(b+1,b+1+n,cmp2);
	int j=1;
	for(int i=1;i<=m;i++)
	{
		for(;j<=n;j++)
			if(a[b[j]]<=w[id[i]]) update(b[j]);
			else break;
		ans[id[i]]=v[id[i]]-u[id[i]]+1-(sum(v[id[i]])-sum(u[id[i]]-1));
	}
	for(int i=1;i<=m;i++)
		fastput(ans[i]);
	
	return 0;
}

主席树做法:

数列离散化,按权值建线段树,建树方式和POJ2104一样,查询的时候如果区间的mid>=x就查询左子树,否则左子树和加上右子树的查询结果。

int a[NN],b[NN],lb,n,m;
struct segmenttree
{
    int l,r,s;
}tree[NN*25];
int node;
int root[NN];
int search(int x)
{
    int l=0,r=lb,mid;
    while (l<r)
    {
        mid=l+r+1>>1;
        if(b[mid]<=x) l=mid;
        else r=mid-1;
    }
    return l;
}
int build(int l,int r)
{
    int k=++node;
    tree[k].s=0;
    if(l==r) return k;
    int m=l+r>>1;
    if(l<=m) tree[k].l=build(l,m);
    if(r>m) tree[k].r=build(m+1,r);
    return k;
}
int change(int rt,int l,int r,int x)
{
    int k=++node,root=k,mid;
    tree[k]=tree[rt];
    tree[k].s++;
    while (l<r)
    {
        mid=l+r>>1;
        if(x<=mid)
        {
            rt=tree[rt].l;
            tree[k].l=++node;
            k=node;
            tree[k]=tree[rt];
            tree[k].s++;
            r=mid;
        }
        else
        {
            rt=tree[rt].r;
            tree[k].r=++node;
            k=node;
            tree[k]=tree[rt];
            tree[k].s++;
            l=mid+1;
        }
    }
    return root;
}
int query(int L,int R,int l,int r,int x)
{
    int mid,ans=0;
    while (l<r)
    {
        mid=l+r>>1;
        if(mid>=x)
        {
            r=mid;
            L=tree[L].l;
            R=tree[R].l;
        }
        else
        {
            l=mid+1;
            ans+=tree[tree[R].l].s-tree[tree[L].l].s;
            L=tree[L].r;
            R=tree[R].r;
        }
    }
    if(l==r)
    {
        if(x<l) return 0;
        else return ans+tree[R].s-tree[L].s;
    }
}
int u,v,w,i;
int main()
{
    int tt=fastget();
    for(int cs=1;cs<=tt;cs++)
    {
        n=fastget();
        m=fastget();
        for(i=1;i<=n;i++)
            b[i]=a[i]=fastget();
        lb=1;
        sort(b+1,b+1+n);
        for(i=2;i<=n;i++)
            if(b[i]!=b[lb]) b[++lb]=b[i];
        node=0;
        root[0]=build(1,lb);
        for(i=1;i<=n;i++)
            root[i]=change(root[i-1],1,lb,search(a[i]));
        printf("Case %d:\n",cs);
        while (m--)
        {
            u=fastget(),v=fastget(),w=fastget();
            u++; v++;
            w=search(w);
            if(w==0) fastput(0);
            else fastput(query(root[u-1],root[v],1,lb,w));
        }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值