划分树
先排序求出排序后的数列
划分树,就是从下向上的递归,每次都把小于或等于(等于的个数是n/2-小于中间数的个数)中间数的数放前。
lg( n ) 的算法。
最基本的划分树题目,求给定区间内的最大的值。
View Code // 划分树 #include<cstdio> #include<iostream> #include<algorithm> #define size 100005 using namespace std; #define inta long long int s[size]; //排序的后的数 int val[25][size]; //记录第k层当前位子的元素值 int num[25][size]; //记录元素所在区间的当前位置之前进入左孩子的个数 void build(int l,int r,int p) { if(l==r) return ; int i,mid=(l+r)>>1,same; same=mid-l+1; for(i=l;i<=r;i++) { if(val[p][i] < s[mid]) same--; } int ln=l,rn=mid+1; for(i=l;i<=r;i++) { if(i==l) num[p][i]=0; else num[p][i]=num[p][i-1]; if(val[p][i]<s[mid]) { num[p][i]++; val[p+1][ln++]=val[p][i]; } else if(val[p][i]>s[mid]) val[p+1][rn++]=val[p][i]; else { if(same>0) { same--; num[p][i]++; val[p+1][ln++]=val[p][i]; } else val[p+1][rn++]=val[p][i]; } } build(l,mid,p+1); build(mid+1,r,p+1); } int query(int li,int ri,int k,int l,int r,int p) { // if(li>ri) return 0; if(l==r) return val[p][li]; int mid=(l+r)>>1; int w1=num[p][ri],w2=0,b1,b2; if(li>l) { w2=num[p][li-1]; //记录区间[l, li-1)中计入左孩子的元素的个数 w1-=w2; // 记录区间[li, ri]中进入左孩子的元素的个数 } if(w1>=k) return query(l+w2,l+w2+w1-1,k,l,mid,p+1); else { b1=li-l-w2; //表示[l, li-1]中分到右孩子的个数 b2=ri-li+1-w1; //表示[li, ri] 中分到右孩子的个数 return query(mid+b1+1,mid+b1+b2,k-w1,mid+1,r,p+1); } } int main() { int i,n,m; int l,r,k; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&s[i]); val[1][i]=s[i]; } sort(s+1,s+1+n); build(1,n,1); for(i=0;i<m;i++) { scanf("%d%d%d",&l,&r,&k); printf("%d\n",query(l,r,k,1,n,1)); } return 0; }
对数据进行四个操作,1)直接把数加到末尾,2,4直接可用划分树求解,而3是划分树的方向应用。
注意::一直wa ,是应为定义了个size,把size 变成max就过了,,气人呀~~~
View Code #include<cstdio> #include<algorithm> #define max 110000 using namespace std; int s[max]; //排序的后的数 int val[25][max]; //记录第k层当前位子的元素值 int num[25][max]; //记录元素所在区间的当前位置之前进入左孩子的个数 struct point { int flag,be,end,k; }; point po[max]; void build(int l,int r,int p) { if(l==r) return ; int i,mid=(l+r)>>1,same; same=mid-l+1; for(i=l;i<=r;i++) { if(val[p][i] < s[mid]) same--; } int ln=l,rn=mid+1; for(i=l;i<=r;i++) { if(i==l) num[p][i]=0; else num[p][i]=num[p][i-1]; if(val[p][i]<s[mid]) { num[p][i]++; val[p+1][ln++]=val[p][i]; } else if(val[p][i]>s[mid]) val[p+1][rn++]=val[p][i]; else { if(same>0) { same--; num[p][i]++; val[p+1][ln++]=val[p][i]; } else val[p+1][rn++]=val[p][i]; } } build(l,mid,p+1); build(mid+1,r,p+1); } int query(int li,int ri,int k,int l,int r,int p) { if(l==r) return val[p][li]; int mid=(l+r)>>1; int w1=num[p][ri],w2=0,b1,b2; if(li>l) { w2=num[p][li-1]; //记录区间[l, li-1)中计入左孩子的元素的个数 w1-=w2; // 记录区间[li, ri]中进入左孩子的元素的个数 } if(w1>=k) return query(l+w2,l+w2+w1-1,k,l,mid,p+1); else { b1=li-l-w2; //表示[l, li-1]中分到右孩子的个数 b2=ri-li+1-w1; //表示[li, ri] 中分到右孩子的个数 return query(mid+b1+1,mid+b1+b2,k-w1,mid+1,r,p+1); } } int getans(int end,int x,int n) { int low=1,high=end,a; while(low<=high) { int m=(low+high)>>1; a=query(1,end,m,1,n,1); if(a<=x) low=m+1; else high=m-1; } return low-1; } int main() { int n,i,m,t; char str[10]; long long ans1,ans2,ans3; int as=1; while(scanf("%d",&m)!=EOF) { n=0;ans1=0;ans2=0;ans3=0;t=0; for(i=0;i<m;i++) { scanf("%s",str); if(str[0]=='I') { ++n; scanf("%d",&s[n]); val[1][n]=s[n]; } else { t++; if(str[6]=='1') { po[t].flag=1; scanf("%d%d%d",&po[t].be,&po[t].end,&po[t].k); } else if(str[6]=='2') { po[t].flag=2; scanf("%d",&po[t].k); po[t].be=1; po[t].end=n; } else { po[t].flag=3; scanf("%d",&po[t].k); po[t].be=1; po[t].end=n; } } } sort(s+1,s+n+1); build(1,n,1); printf("Case %d:\n",as++); for(i=1;i<=t;i++) { if(po[i].flag==1) ans1+=query(po[i].be,po[i].end,po[i].k,1,n,1); else if(po[i].flag==2) ans2+=getans(po[i].end,po[i].k,n); else ans3+=query(po[i].be,po[i].end,po[i].k,1,n,1); } printf("%I64d\n%I64d\n%I64d\n",ans1,ans2,ans3); } return 0; }
这题除了划分树,考的就是划分树中sum的活用 ,sum为[ l , r ]中的小于第k个数的和。
这题有两个注意:first:她所求的x就是中位数,奇数是中间那个,偶数就是中间两个的任意一个,画个数轴就可以证明。
second:注意和的范围是超出乐 int 整型,在一些转换中,要注意强制转换。
View Code #include<cstdio> #include<iostream> #include<algorithm> #define size 100010 #define LL long long using namespace std; int s[size]; //排序的后的数 int val[20][size]; //记录第k层当前位子的元素值 int num[20][size]; //记录元素所在区间的当前位置之前进入左孩子的个数 LL sum[20][size]; //记录比当前元素小的元素的和 LL Sum; void build(int l,int r,int p) { if(l==r) return ; int i,mid=(l+r)>>1,same; same=mid-l+1; for(i=l;i<=r;i++) { if(val[p][i] < s[mid]) same--; } int ln=l,rn=mid+1; for(i=l;i<=r;i++) { if(i==l) { num[p][i]=0; sum[p][i]=0; } else { num[p][i]=num[p][i-1]; sum[p][i]=sum[p][i-1]; } if(val[p][i]<s[mid]) { num[p][i]++; sum[p][i]+=val[p][i]; val[p+1][ln++]=val[p][i]; } else if(val[p][i]>s[mid]) val[p+1][rn++]=val[p][i]; else { if(same>0) { same--; num[p][i]++; sum[p][i]+=val[p][i]; val[p+1][ln++]=val[p][i]; } else val[p+1][rn++]=val[p][i]; } } build(l,mid,p+1); build(mid+1,r,p+1); } int query(int li,int ri,int k,int l,int r,int p) { if(l==r) return val[p][li]; int mid=(l+r)>>1; int w1=num[p][ri],w2=0,b1,b2; LL w3=sum[p][ri]; if(li>l) { w2=num[p][li-1]; w1-=w2; w3-=sum[p][li-1]; } if(w1>=k) return query(l+w2,l+w2+w1-1,k,l,mid,p+1); else { b1=li-l-w2; b2=ri-li+1-w1; Sum+=w3; return query(mid+b1+1,mid+b1+b2,k-w1,mid+1,r,p+1); } } int main() { int t,n,q,i,k; int l,r; LL ans; scanf("%d",&t); for(int as=1;as<=t;as++) { scanf("%d",&n); sum[0][0]=0; for(i=1;i<=n;i++) { scanf("%d",&s[i]); val[1][i]=s[i]; sum[0][i]=s[i]+sum[0][i-1]; } sort(s+1,s+n+1); build(1,n,1); scanf("%d",&q); printf("Case #%d:\n",as); for(i=0;i<q;i++) { Sum=0; scanf("%d%d",&l,&r); l++;r++;k=(r-l)/2+1; LL a=(LL)query(l,r,k,1,n,1); ans=a*(k-1)-Sum; ans+=(sum[0][r]-sum[0][l-1])-Sum-a-a*(r-l+1-k); printf("%I64d\n",ans); } printf("\n"); } return 0; }
划分树+二分查找 (此题还可以用线段树求解,见线段树专题)
View Code #include<stdio.h> #include<algorithm> #define size 100005 using namespace std; int s[size]; //排序的后的数 int val[25][size]; //记录第k层当前位子的元素值 int num[25][size]; //记录元素所在区间的当前位置之前进入左孩子的个数 void build(int l,int r,int p) { if(l==r) return ; int i,mid=(l+r)>>1,same; same=mid-l+1; for(i=l;i<=r;i++) { if(val[p][i] < s[mid]) same--; } int ln=l,rn=mid+1; for(i=l;i<=r;i++) { if(i==l) num[p][i]=0; else num[p][i]=num[p][i-1]; if(val[p][i]<s[mid]) { num[p][i]++; val[p+1][ln++]=val[p][i]; } else if(val[p][i]>s[mid]) val[p+1][rn++]=val[p][i]; else { if(same>0) { same--; num[p][i]++; val[p+1][ln++]=val[p][i]; } else val[p+1][rn++]=val[p][i]; } } build(l,mid,p+1); build(mid+1,r,p+1); } int query(int li,int ri,int k,int l,int r,int p) { // if(li>ri) return 0; if(l==r) return val[p][li]; int mid=(l+r)>>1; int w1=num[p][ri],w2=0,b1,b2; if(li>l) { w2=num[p][li-1]; w1-=w2; } if(w1>=k) return query(l+w2,l+w2+w1-1,k,l,mid,p+1); else { b1=li-l-w2; b2=ri-li+1-w1; return query(mid+b1+1,mid+b1+b2,k-w1,mid+1,r,p+1); } } void find(int l,int r,int h,int n) { int low=1,high=r-l+1,a; while(low<=high) { int m=(low+high)>>1; a=query(l,r,m,1,n,1); if(a<=h) low=m+1; else high=m-1; } printf("%d\n",low-1); } int main() { int t,n,m,i; int l,r,h; scanf("%d",&t); for(int as=1;as<=t;as++) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&s[i]); val[1][i]=s[i]; } sort(s+1,s+n+1); build(1,n,1); printf("Case %d:\n",as); while(m--) { scanf("%d%d%d",&l,&r,&h); l++;r++; if(query(l,r,r-l+1,1,n,1)<=h) printf("%d\n",r-l+1); else if(query(l,r,1,1,n,1)>h) printf("0\n"); else find(l,r,h,n); } } return 0; }
还有:hdu 4251


浙公网安备 33010602011771号