划分树

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

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

划分树主要是用来求 在一个区间[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 }

你可能感兴趣的:(树)