最近几天状态不佳,没事的时候总想看下奥运会。。

划分树看几天了,今天终于看完了。

划分树主要是用来求 在一个区间[l,r]中第k大的数。

以及小于当前数的和。

首先看一道题目:

http://acm.hdu.edu.cn/showproblem.php?pid=4251

 

hdu 4251 The Famous ICPC Team Again

非常裸的模板题,给n个数,m次询问,每次询问一个区间,输出该区间中间大的值。

/*
划分树:在区间[l,r]内找第k大的数
*/
# include<stdio.h>
# include<string.h>
# include<stdlib.h>
# define N 100005
int b[N];
struct node{
    int l,r;
}tree[4*N];
int val[20][N];
int num[20][N];
int VAL;
int cmp(const void *a1,const void *b1)
{
    return *(int *)a1 - *(int *)b1;
}
void bulid(int l,int r,int h,int t)
{
    int i,mid;
    int ans,ans1,ans2;
    int count=0,count1;
    if(l>r) return;
    tree[t].l=l;
    tree[t].r=r;
    if(l==r) return;
    
    mid=(l+r)/2;
    ans=b[mid];
    
    for(i=l;i<=r;i++)
        if(val[h][i]<ans) count++;
        ans1=l;
        ans2=mid+1;
        count1=0;//表示与中间值b[mid]相等的点有多少个进入到了右孩子

        for(i=l;i<=r;i++)
        {
            if(val[h][i]<ans) val[h+1][ans1++]=val[h][i];
            else if(val[h][i]==ans && count1<mid-l+1-count) {count1++;val[h+1][ans1++]=val[h][i];}
            else val[h+1][ans2++]=val[h][i];
            num[h][i]=ans1-l;
        }
        bulid(l,mid,h+1,2*t);
        bulid(mid+1,r,h+1,2*t+1);
}
void find(int t,int h,int from,int to,int ans)//在该区间的第from个数和第to个数之间找第ans大的数
{
    int l,r;
    int ans1,ans2;
    if(tree[t].l==tree[t].r) {VAL=val[h][tree[t].l];return;}
    l=tree[t].l;
    r=tree[t].r;
    if(from>1) ans1=num[h][from+l-2];//from的前一个点(包括前一个点)之前有多少个点在右子树里面
    else ans1=0; 
    ans2=num[h][to+l-1];//to之前有多少个点(包含to点)在右子树里面
    if(ans2-ans1>=ans) find(2*t,h+1,ans1+1,ans2,ans);
    else find(2*t+1,h+1,from-1-ans1+1,to-ans2,ans-(ans2-ans1));
}
int main()
{
    int i,n,m,ncase=0;
    int from,to;
    while(scanf("%d",&n)!=EOF)
    {
        ncase++;
        for(i=1;i<=n;i++)
        {
            scanf("%d",&val[1][i]);
            b[i]=val[1][i];
        }
        qsort(b+1,n,sizeof(b[1]),cmp);
        bulid(1,n,1,1);
        for(i=1;i<20;i++) num[i][0]=0;
        scanf("%d",&m);
        printf("Case %d:\n",ncase);
        while(m--)
        {
            scanf("%d%d",&from,&to);
            find(1,1,from,to,(to-from)/2+1);
            printf("%d\n",VAL);
        }
    }
    return 0;
}

 

再来一个求前k个数的和的题目。

http://acm.hdu.edu.cn/showproblem.php?pid=3473

hdu 3473 Minimum Sum

给n个数,然后也是m次询问,每次询问一个区间[l,r],在区间中找一个数x使最小。

可以证明,当l----r之间数的个数为奇数时,x为中间大的时候该表达式值最小。

为偶数时,x为中间的两个数的其中一个时值最小。

求出前k个数的和,然后改区间所有数的总和可以求出,这样一搞就能把表达式的值求出来。

View Code
  1 # include<stdio.h>
  2 # include<string.h>
  3 # include<stdlib.h>
  4 # define N 100005
  5 struct node{
  6     int l,r;
  7 }tree[4*N];
  8 int val[20][N];
  9 int num[20][N];//第i个点前面有多少个点进入到了右子树(包括第i个点)
 10 __int64 sum[20][N];//记录比当前元素小的元素和
 11 int a[N];
 12 __int64 VAL,SUM;
 13 int cmp(const void *a1,const void *b1)
 14 {
 15     return *(int *)a1 > *(int *)b1 ? 1 : -1;
 16 }
 17 void bulid(int l,int r,int h,int t)
 18 {
 19     int i,mid,count;
 20     int count1,ans1,ans2;
 21     tree[t].l=l;
 22     tree[t].r=r;
 23     if(l==r) return;
 24     mid=(l+r)/2;
 25     count=0;
 26     for(i=l;i<=r;i++)
 27         if(val[h][i]<a[mid]) count++; 
 28     //count记录该区间比中间值小的元素的个数,那进入右孩子的与中间值相等的点的个数是mid-l+1-count;
 29     ans1=l;
 30     ans2=mid+1;
 31     count1=0;
 32     for(i=l;i<=r;i++)
 33     {
 34         if(i==l) sum[h][i]=0;
 35         else sum[h][i]=sum[h][i-1];
 36         if(val[h][i]<a[mid])
 37         {
 38             val[h+1][ans1++]=val[h][i];
 39             sum[h][i]+=val[h][i];
 40         }
 41         else if(val[h][i]==a[mid] && count1<mid-l+1-count)
 42         {
 43             count1++;
 44             val[h+1][ans1++]=val[h][i];
 45             sum[h][i]+=val[h][i];
 46         }
 47         else val[h+1][ans2++]=val[h][i];
 48         num[h][i]=ans1-l;
 49     }
 50     bulid(l,mid,h+1,2*t);
 51     bulid(mid+1,r,h+1,2*t+1);
 52 }
 53 void find(int t,int h,int from,int to,int k)
 54 {
 55     int ans1,ans2;
 56     __int64 sum1,sum2;
 57     int l,r;
 58     l=tree[t].l;
 59     r=tree[t].r;
 60     if(l==r) {SUM+=val[h][l];VAL=val[h][l];return;}
 61     if(from==1) ans1=0;
 62     else ans1=num[h][from+l-2];
 63     ans2=num[h][to+l-1];
 64     if(ans2-ans1>=k) find(2*t,h+1,ans1+1,ans2,k);
 65     else
 66     {
 67         find(2*t+1,h+1,from-1-ans1+1,to-ans2,k-(ans2-ans1));///////////一定要注意,两次都写错了
 68         if(from==1) sum1=0;
 69         else sum1=sum[h][l+from-2];
 70         sum2=sum[h][to+l-1];
 71         SUM+=(sum2-sum1);
 72     }
 73 }
 74 int main()
 75 {
 76     int i,j,n,ncase,t,m;
 77     __int64 ans1,ans2,val1,val2,ans;
 78     __int64 Max1,Max2;
 79     int from,to,k;
 80     scanf("%d",&ncase);
 81     for(t=1;t<=ncase;t++)
 82     {
 83         scanf("%d",&n);
 84         for(i=1;i<=n;i++)
 85         {
 86             scanf("%d",&val[1][i]);
 87             a[i]=val[1][i];
 88         }
 89         qsort(a+1,n,sizeof(a[1]),cmp);
 90         bulid(1,n,1,1);
 91         scanf("%d",&m);
 92         printf("Case #%d:\n",t);
 93         while(m--)
 94         {
 95             scanf("%d%d",&from,&to);
 96             from++;
 97             to++;
 98             if((to-from)%2==0)
 99             {
100                 SUM=0;
101                 k=(to-from)/2+1;
102                 find(1,1,from,to,(to-from)/2+1);
103                 ans1=SUM;
104                 val1=VAL;
105                 SUM=0;
106                 find(1,1,from,to,to-from+1);
107                 ans2=SUM;
108                 val2=VAL;
109                 ans=val1*k-ans1+ans2-ans1-(k-1)*val1;
110                 printf("%I64d\n",ans);
111             }
112             else
113             {
114                 SUM=0;
115                 k=(to-from+1)/2;
116                 find(1,1,from,to,k);
117                 ans1=SUM;
118                 val1=VAL;
119                 SUM=0;
120                 find(1,1,from,to,to-from+1);
121                 ans2=SUM;
122                 val2=VAL;
123                 Max1=val1*k-ans1+ans2-ans1-k*val1;
124 
125                 SUM=0;
126                 k++;
127                 find(1,1,from,to,k);
128                 ans1=SUM;
129                 val1=VAL;
130                 Max2=val1*k-ans1+ans2-ans1-(k-2)*val1;
131                 printf("%I64d\n",Max1<Max2?Max1:Max2);
132 
133             }
134         }
135         printf("\n");
136     }
137     return 0;
138 }

 

再来一个加强版的

http://codeforces.com/problemset/problem/182/C

Optimal Sum

有n个数,然后有[1,len],[2,len+1],[3,len+2]...[n-len+1,n] 这么多区间。

每个区间最多改变其中的k个数,问最后每个区间包含的数的和的绝对值 最大值。

改变一个数可以把一个数变成他的相反数,正数变为负数,负数变为正数。

如果我们想通过改变负数最后是他们的和达到一个比较大的值,那我们必须改变绝对值比较大的k个负数,也就是区间中比较小的k个负数。

如果改变正数,那就改变比较大的k个正数。

这样就转化成了求前k个数的和的问题了。

View Code
  1 # include<stdio.h>
  2 # include<string.h>
  3 # include<stdlib.h>
  4 # define N 100005
  5 struct node{
  6     int l,r;
  7 }tree[4*N];
  8 int val[20][N];
  9 int num[20][N];//第i个点前面有多少个点进入到了右子树(包括第i个点)
 10 __int64 sum[20][N];//记录比当前元素小的元素和
 11 int a[N];
 12 __int64 VAL,SUM;
 13 struct node1{
 14     int count1;//负数的个数
 15     int count2;//正数的个数
 16     __int64 sum1;//负数的和
 17     __int64 sum2;//正数的和
 18 }s[N];
 19 int cmp(const void *a1,const void *b1)
 20 {
 21     return *(int *)a1 - *(int *)b1;
 22 }
 23 void bulid(int l,int r,int h,int t)
 24 {
 25     int i,mid,count;
 26     int count1,ans1,ans2;
 27     tree[t].l=l;
 28     tree[t].r=r;
 29     if(l==r) return;
 30     mid=(l+r)/2;
 31     count=0;
 32     for(i=l;i<=r;i++)
 33         if(val[h][i]<a[mid]) count++; 
 34         //count记录该区间比中间值小的元素的个数,那进入右孩子的与中间值相等的点的个数是mid-l+1-count;
 35         ans1=l;
 36         ans2=mid+1;
 37         count1=0;
 38         for(i=l;i<=r;i++)
 39         {
 40             if(i==l) sum[h][i]=0;
 41             else sum[h][i]=sum[h][i-1];
 42             if(val[h][i]<a[mid])
 43             {
 44                 val[h+1][ans1++]=val[h][i];
 45                 sum[h][i]+=val[h][i];
 46             }
 47             else if(val[h][i]==a[mid] && count1<mid-l+1-count)
 48             {
 49                 count1++;
 50                 val[h+1][ans1++]=val[h][i];
 51                 sum[h][i]+=val[h][i];
 52             }
 53             else val[h+1][ans2++]=val[h][i];
 54             num[h][i]=ans1-l;
 55         }
 56         bulid(l,mid,h+1,2*t);
 57         bulid(mid+1,r,h+1,2*t+1);
 58 }
 59 void find(int t,int h,int from,int to,int k)
 60 {
 61     int ans1,ans2;
 62     __int64 sum1,sum2;
 63     int l,r;
 64     l=tree[t].l;
 65     r=tree[t].r;
 66     if(l==r) {SUM+=val[h][l];VAL=val[h][l];return;}
 67     if(from==1) ans1=0;
 68     else ans1=num[h][from+l-2];
 69     ans2=num[h][to+l-1];
 70     if(ans2-ans1>=k) find(2*t,h+1,ans1+1,ans2,k);
 71     else
 72     {
 73         find(2*t+1,h+1,from-1-ans1+1,to-ans2,k-(ans2-ans1));///////////一定要注意,两次都写错了
 74         if(from==1) sum1=0;
 75         else sum1=sum[h][l+from-2];
 76         sum2=sum[h][to+l-1];
 77         SUM+=(sum2-sum1);
 78     }
 79 }
 80 int main()
 81 {
 82     int i,j,n,ncase,t,K,len,mm;
 83     __int64 ans1,ans2,val1,val2,ans;
 84     __int64 Max1,Max2;
 85     int from,to,k;
 86 
 87     while(scanf("%d%d",&n,&len)!=EOF)
 88     {
 89         for(i=1;i<=n;i++)
 90         {
 91             scanf("%d",&val[1][i]);
 92             a[i]=val[1][i];
 93         }
 94         qsort(a+1,n,sizeof(a[1]),cmp);
 95         bulid(1,n,1,1);
 96         scanf("%d",&K);
 97         s[1].count1=0;
 98         s[1].count2=0;
 99         s[1].sum1=0;
100         s[1].sum2=0;
101         for(i=1;i<=len;i++)
102         {
103             if(val[1][i]<0) {s[1].count1++;s[1].sum1+=val[1][i];}
104             else if(val[1][i]>0) {s[1].count2++;s[1].sum2+=val[1][i];}
105         }
106         Max1=-1;
107         for(i=1;i<=n-len+1;i++)
108         {
109             if(i!=1)
110             {
111                 s[i]=s[i-1];
112                 if(val[1][i-1]<0) 
113                 {
114                     s[i].count1--;
115                     s[i].sum1-=val[1][i-1];
116                 }
117                 else if(val[1][i-1]>0)
118                 {
119                     s[i].count2--;
120                     s[i].sum2-=val[1][i-1];
121                 }
122                 if(val[1][i+len-1]<0)
123                 {
124                     s[i].count1++;
125                     s[i].sum1+=val[1][i+len-1];
126                 }
127                 else if(val[1][i+len-1]>0)
128                 {
129                     s[i].count2++;
130                     s[i].sum2+=val[1][i+len-1];
131                 }
132             }
133             Max2=s[i].sum1+s[i].sum2;
134             if(Max2<0) Max2=-Max2;
135             if(Max2>Max1) Max1=Max2;
136             if(K==0) continue;
137             if(s[i].count1!=0)
138             {
139                 mm=K<s[i].count1?K:s[i].count1;
140                 SUM=0;
141                 find(1,1,i,i+len-1,mm);
142                 ans1=SUM;
143                 ans1=-ans1;
144                 Max2=s[i].sum1+ans1+ans1+s[i].sum2;
145                 if(Max2<0) Max2=-Max2;
146                 if(Max2>Max1) Max1=Max2;
147             }
148             if(s[i].count2!=0)
149             {
150                 //SUM=0;
151                 //find(1,1,i,i+len-1,len);
152                 ans1=s[i].sum1+s[i].sum2;
153                 mm=K<s[i].count2?K:s[i].count2;
154                 SUM=0;
155                 find(1,1,i,i+len-1,len-mm+1);
156                 ans2=SUM;
157                 val2=VAL;
158                 ans=ans1-ans2+val2;
159                 Max2=s[i].sum1+s[i].sum2-ans-ans;
160                 if(Max2<0) Max2=-Max2;
161                 if(Max2>Max1) Max1=Max2;
162             }
163         }
164         printf("%I64d\n",Max1);
165     }
166     return 0;
167 }
posted on 2012-08-01 14:47  奋斗青春  阅读(1079)  评论(0编辑  收藏  举报