最近几天状态不佳,没事的时候总想看下奥运会。。
划分树看几天了,今天终于看完了。
划分树主要是用来求 在一个区间[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个数的和,然后改区间所有数的总和可以求出,这样一搞就能把表达式的值求出来。
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个数的和的问题了。
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 }