HDU 4417 Super Mario(划分树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4417

题意:给出一个数列,若干询问,L,R,h,询问区间[L,R]中小于等于h的数字有多少个?

思路:比赛时我用的树状数组A的。后来发现有人用划分树,我是第一次看到划分树,就学习一下。但是查询的时候,划分树查询的是区间内第K小的数,而此题要小于等于某个值h的所有数字,所以二分K,将返回值与h比较即可。

 #include <iostream>
 #include <cstdio>
 #include <algorithm>
 using namespace std;
 
 
 const int MAX=100005;
 
 struct Node
 {
     int L,R;
 };
 
 struct HuaFen_tree
 {
     Node a[MAX<<2];
     int s[MAX],t[35][MAX],tot[35][MAX];
 
     void input(int n)
     {
         int i;
         for(i=1;i<=n;i++)
         {
             scanf("%d",&s[i]);
             t[1][i]=s[i];
         }
         sort(s+1,s+n+1);
     }
     void build(int dep,int u,int L,int R)
     {
         a[u].L=L;
         a[u].R=R;
         if(L==R) return;
         int i,mid=(L+R)>>1;
         int sameNum=mid-L+1;
         for(i=L;i<=R;i++) if(t[dep][i]<s[mid]) sameNum--;
         int LL=L,LR=mid,RL=mid+1,RR=R;
         int Lnum=0,Rnum=0;
         for(i=L;i<=R;i++)
         {
             if(i==L) tot[dep][i]=0;
             else tot[dep][i]=tot[dep][i-1];
             if(t[dep][i]<s[mid])
             {
                 tot[dep][i]++;
                 t[dep+1][LL+Lnum]=t[dep][i];
                 Lnum++;
             }
             else if(t[dep][i]>s[mid])
             {
                 t[dep+1][RL+Rnum]=t[dep][i];
                 Rnum++;
             }
             else
             {
                 if(sameNum>0)
                 {
                     sameNum--;
                     tot[dep][i]++;
                     t[dep+1][LL+Lnum]=t[dep][i];
                     Lnum++;
                 }
                 else
                 {
                     t[dep+1][RL+Rnum]=t[dep][i];
                     Rnum++;
                 }
             }
         }
         build(dep+1,u<<1,LL,LR);
         build(dep+1,u<<1|1,RL,RR);
     }
 
     //在区间[a[u].L,a[u].R]这个区间中查找[L,R]中的第K大值
     int query(int dep,int u,int L,int R,int K)
     {
         if(L==R) return t[dep][L];
         int x,y,xx,yy,_L,_R,mid=(a[u].L+a[u].R)>>1;
         if(L==a[u].L) x=0;
         else x=tot[dep][L-1]; //x为[a[u].L,L-1]中分到左边的
         y=tot[dep][R]-x;      //y为[L,R]中分到左边的
         if(y>=K)
         {
             _L=a[u].L+x;
             _R=a[u].L+x+y-1;
             return query(dep+1,u<<1,_L,_R,K);
         }
         else
         {
             xx=L-a[u].L-x;  //xx是[a[u].L,L-1]中分到右边的
             yy=R-L+1-y;     //yy是[L,R]中分到右边的
             _L=mid+1+xx;
             _R=mid+1+xx+yy-1;
             return query(dep+1,u<<1|1,_L,_R,K-y);
         }
     }
 };
 
 HuaFen_tree a;
 int n,m,C,num=0;
 
 int find(int sum,int h,int L,int R)
 {
     int low=1,high=sum,mid;
     while(low<=high)
     {
         mid=(low+high)>>1;
         if(a.query(1,1,L,R,mid)>h) high=mid-1;
         else low=mid+1;
     }
     if(a.query(1,1,L,R,low)<=h) return low;
     return high;
 }
 
 int main()
 {
     for(scanf("%d",&C);C--;)
     {
         scanf("%d%d",&n,&m);
         a.input(n);
         a.build(1,1,1,n);
         int L,R,h,ans;
         printf("Case %d:\n",++num);
         while(m--)
         {
             scanf("%d%d%d",&L,&R,&h);
             L++;
             R++;
             if(a.query(1,1,L,R,R-L+1)<=h) ans=R-L+1;
             else if(a.query(1,1,L,R,1)>h) ans=0;
             else ans=find(R-L+1,h,L,R);
             printf("%d\n",ans);
         }
     }
     return 0;
 }

  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值