HDU 3473 Minimum Sum(划分树)

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

题意:给出一个数列,每次给出一个区间[l,r],求一个数x使得最小?

思路:将划分树的每层像tot[dep][i]一样,增加一个sum[dep][i],表示[L,i]之间的数字中分到左子树的所有数字之和。一个明显的结论是一定是找[L,R]区间排序后的中位数作为x可使得结果最小。这样,每次询问首先求得区间的中位数,并求出中位数之前的所有数字之和,则剩下的就是中位数前后两段与中位数做差再加和。

 #include <iostream>

 #include <cstdio>

 #include <algorithm>

 #define int64 __int64

 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];

     int64 sum[35][MAX],SUM[MAX],leftSum;

 

     void input(int n)

     {

         int i;

         SUM[0]=0;

         for(i=1;i<=n;i++)

         {

             scanf("%d",&s[i]);

             t[1][i]=s[i];

             SUM[i]=SUM[i-1]+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;

                 sum[dep][i]=0;

             }

             else

             {

                 tot[dep][i]=tot[dep][i-1];

                 sum[dep][i]=sum[dep][i-1];

             }

             if(t[dep][i]<s[mid])

             {

                 tot[dep][i]++;

                 t[dep+1][LL+Lnum]=t[dep][i];

                 Lnum++;

                 sum[dep][i]+=t[dep][i];

             }

             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++;

                     sum[dep][i]+=t[dep][i];

                 }

                 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;

         int64 p;

         if(L==a[u].L)

         {

             x=0;

             p=0;

         }

         else

         {

             x=tot[dep][L-1]; //x为[a[u].L,L-1]中分到左边的

             p=sum[dep][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;

             leftSum+=sum[dep][R]-p;

             return query(dep+1,u<<1|1,_L,_R,K-y);

         }

     }

     int64 cal(int L,int R)

     {

         leftSum=0;

         int mid=(R-L+2)/2;

         int64 k=query(1,1,L,R,mid);

         int64 ans=k*(mid-1)-leftSum;

         ans+=SUM[R]-SUM[L-1]-leftSum-k-(R-L+1-mid)*k;

         return ans;

     }

 };

 

 HuaFen_tree a;

 int n,m,C,num=0;

 

 

 int main()

 {

     for(scanf("%d",&C);C--;)

     {

         scanf("%d",&n);

         a.input(n);

         a.build(1,1,1,n);

         int L,R;

         int64 ans;

         printf("Case #%d:\n",++num);

         for(scanf("%d",&m);m--;)

         {

             scanf("%d%d",&L,&R);

             ans=a.cal(L+1,R+1);

             printf("%I64d\n",ans);

         }

         puts("");

     }

     return 0;

 }

  

 

 

你可能感兴趣的:(HDU)