hdu 3727 Jewel 划分数+树状数组 求区间和当前段的第k大数

Problem Description
Jimmy wants to make a special necklace for his girlfriend. He bought many beads with various sizes, and no two beads are with the same size. Jimmy can't remember all the details about the beads, for the necklace is so long. So he turns to you for help.

Initially, there is no bead at all, that is, there is an empty chain. Jimmy always sticks the new bead to the right of the chain, to make the chain longer and longer. We number the leftmost bead as Position 1, and the bead to its right as Position 2, and so on. Jimmy usually asks questions about the beads' positions, size ranks and actual sizes. Specifically speaking, there are 4 kinds of operations you should process:

Insert x 
Put a bead with size x to the right of the chain (0 < x < 231, and x is different from all the sizes of beads currently in the chain)
Query_1 s t k 
Query the k-th smallest bead between position s and t, inclusive. You can assume 1 <= s <= t <= L, (L is the length of the current chain), and 1 <= k <= min (100, t-s+1)
Query_2 x
Query the rank of the bead with size x, if we sort all the current beads by ascent order of sizes. The result should between 1 and L (L is the length of the current chain)
Query_3 k
Query the size of the k-th smallest bead currently (1 <= k <= L, L is the length of the current chain)

 

Input
There are several test cases in the input. The first line for each test case is an integer N, indicating the number of operations. Then N lines follow, each line contains one operation, as described above. 

You can assume the amount of "Insert" operation is no more than 100000, and the amounts of "Query_1", "Query_2" and "Query_3" are all less than 35000.
There are several test cases in the input. The first line for each test case is an integer N, indicating the number of operations. Then N lines follow, each line contains one operation, as described above. 

You can assume the amount of "Insert" operation is no more than 100000, and the amounts of "Query_1", "Query_2" and "Query_3" are all less than 35000.Query the rank of the bead with size x, if we sort all the current beads by ascent order of sizes. The result should between 1 and L (L is the length of the current chain)
Query_3 k
Query the size of the k-th smallest bead currently (1 <= k <= L, L is the length of the current chain)

 

Output
Output 4 lines for each test case. The first line is "Case T:", where T is the id of the case. The next 3 lines indicate the sum of results for Query_1, Query_2 and Query_3, respectively. 

 

Sample Input
  
  
10 Insert 1 Insert 4 Insert 2 Insert 5 Insert 6 Query_1 1 5 5
 

Sample Output
  
  
Case 1: 10 3 5
Hint
The answers for the 5 queries are 6, 4, 3, 4, 1, respectively.

//


#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<climits>
using namespace std;
const int maxn=200000;


//init
int q;
char str[20];
int it[maxn][4];
int kind[maxn];
//离散化 从1开始
int tnum[maxn];
//元素个数
int n;
int posi(int x)//比hash快很多
{
    int l=1,r=n;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(tnum[mid]==x) return mid;
        else if(tnum[mid]>x) r=mid;
        else l=mid+1;
    }
    return r;
}
void lsh()
{
    sort(tnum+1,tnum+1+n);//此题中没有重复元素
}
//树状数组
int c[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x,int val)
{
    for(int i=x;i<=n;i+=lowbit(i))
    {
        c[i]+=val;
    }
}
int getsum(int x)
{
    int cnt=0;
    for(int i=x;i>=1;i-=lowbit(i))
    {
        cnt+=c[i];
    }
    return cnt;
}
//划分树
const int N=101000;
const int DEEP=20;//划分树最多层数
struct Node
{
    int l,r;
};
Node tree[N*4];//线段树
int data[N];//数据
int seg[DEEP][N];//划分树
int LessMid[DEEP][N];//表示在[L,R]内有几个数小于等于date[mid]
void buildtree(int root,int l,int r,int d)//节点,左右区间,层数
{
    tree[root].l=l,tree[root].r=r;
    if(l==r) return ;//叶子节点
    int mid=(l+r)>>1;
    int lsame=mid-l+1;//左面最多可放几个与data[mid]相同的数
    for(int i=l;i<=r;i++)//得出实际能放几个相同的数
    {
        if(seg[d][i]<data[mid]) lsame--;
    }
    int tl=l,tr=mid+1,same=0;//划分树 ,tl,tr表示数的左右子树的起点
    for(int i=l;i<=r;i++)
    {
        if(i==l) LessMid[d][i]=0;//表示在[L,R]内有几个数小于等于date[mid]
        else LessMid[d][i]=LessMid[d][i-1];
        if(seg[d][i]<data[mid]) LessMid[d][i]++,seg[d+1][tl++]=seg[d][i];//划分到左子树
        else if(seg[d][i]>data[mid]) seg[d+1][tr++]=seg[d][i];//划分到右子树
        else //相等情况
        {
            if(same<lsame) same++,LessMid[d][i]++,seg[d+1][tl++]=seg[d][i];//左子树未满
            else seg[d+1][tr++]=seg[d][i];
        }
    }
    buildtree(root<<1,l,mid,d+1);
    buildtree((root<<1)+1,mid+1,r,d+1);
}
//查询[l,r]中的第k小数
int Query(int root,int l,int r,int d,int cnt)//节点,要查询的区间,层数,第cnt小数
{
    if(l==r) return seg[d][l];
    int s;//表示在[l,r]中有几个小于等于data[mid]的个数
    int ss;//表示在[tree[root].l,l-1]中有几个小于等于data[mid]的个数
    if(l==tree[root].l) s=LessMid[d][r],ss=0;
    else s=LessMid[d][r]-LessMid[d][l-1],ss=LessMid[d][l-1];
    if(s>=cnt) return Query(root<<1,tree[root].l+ss,tree[root].l+ss+s-1,d+1,cnt);
    else
    {
        int mid=(tree[root].l+tree[root].r)>>1;
        int bb=l-tree[root].l-ss;//表示[tree[root].l,l-1]中有多少个分到右面
        int b=r-l+1-s;//表示[l,r]有多少个分到右面
        return Query((root<<1)+1,mid+bb+1,mid+bb+b,d+1,cnt-s);
    }
}
int main()
{
    int pl=1;
    while(scanf("%d",&q)==1)
    {
        //init
        n=0;
        //input
        for(int i=0;i<q;i++)
        {
            scanf("%s",str);
            int len=strlen(str);
            if(str[len-1]=='1') scanf("%d%d%d",&it[i][0],&it[i][1],&it[i][2]);
            else scanf("%d",&it[i][0]);
            if(str[len-1]=='t')
            {
                kind[i]=0;
                ++n;
                seg[1][n]=data[n]=tnum[n]=it[i][0];
            }
            else kind[i]=str[len-1]-'0';
        }
        //离散化
        lsh();
        //划分数init
        sort(data+1,data+1+n);
        buildtree(1,1,n,1);
        //树状数组init
        memset(c,0,sizeof(c));
        //查询Qi和
        __int64 res1=0,res2=0,res3=0;
        for(int i=0;i<q;i++)
        {
            if(kind[i]==0)//末尾插入
            {
                update(posi(it[i][0]),1);
            }
            else if(kind[i]==1)//区间第k小数
            {
                int cnt=Query(1,it[i][0],it[i][1],1,it[i][2]);
                res1+=cnt;
            }
            else if(kind[i]==2)//x排第几
            {
                int cnt=getsum(posi(it[i][0]));
                res2+=cnt;
            }
            else//第k小数
            {
                int l=0,r=n;
                while(l<r)
                {
                    int mid=(l+r)>>1;
                    if(getsum(mid)>=it[i][0]) r=mid;
                    else l=mid+1;
                }
                res3+=tnum[r];
            }
        }
        printf("Case %d:\n",pl++);
        printf("%I64d\n%I64d\n%I64d\n",res1,res2,res3);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值