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;

 }

  

 

你可能感兴趣的:(super)