做回菜鸟,老老实实重新学起:
划分树
数据结构;
求k大值及左右和模版:
#define N 123456
int sorted[N]={0}; //对原集合中元素排序后的值
int val[20][N]={0}; //val记录第k层当前位置的值
int num[20][N]={0}; //记录元素所在区间当前位置前的元素进入到左子树的个数
int lnum, rnum; //询问区间里面k-th数左侧和右侧的数的个数
long long sum[20][N]={0}; //记录比当前元素小的元素的和
long long lsum, rsum; //询问区间里面k-th数左侧数之和与右侧数之和
void build(int l, int r, int d)
{
if (l == r) return ;
int mid = (l + r) >> 1;
int same = mid - l + 1;
for (int i=l; i<=r; i++)
if (val[d][i] > sorted[mid])
same--;
int lp = l, rp = mid+1;
for (int i=l; i<=r; i++) {
if (i == l) {
num[d][i] = 0;
sum[d][i] = 0;
} else {
num[d][i] = num[d][i-1];
sum[d][i] = sum[d][i-1];
}
if (val[d][i] > sorted[mid]) {
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else if (val[d][i] < sorted[mid])
val[d+1][rp++] = val[d][i];
else {
if (same) {
same--;
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else val[d+1][rp++] = val[d][i];
}
}
build(l, mid, d+1);
build(mid+1, r, d+1);
}
int query(int a, int b, int k, int l, int r, int d)
{
if (a == b)
return val[d][a];
int mid = (l + r) >> 1;
int s, ss;
long long sss;
if (a == l)
{
s = num[d][b];
ss = 0;
sss = sum[d][b];
}
else
{
s = num[d][b] - num[d][a-1];
ss = num[d][a-1];
sss = sum[d][b] - sum[d][a-1];
}
if (s >= k)
{
a = l + ss;
b = l + ss + s - 1;
return query(a, b, k, l, mid, d+1);
}
else
{
lnum += s;
lsum += sss;
a = mid+1 + a - l - ss;
b = mid+1 + b - l - num[d][b];
return query(a, b, k-s, mid+1, r, d+1);
}
}
bool cmp(int a,int b)
{ return a>b; }
void solve(int n,int m)
{
long long s[N]={0};
for(int i=1;i<=n;i++)
{
scanf("%d",&sorted[i]);
val[0][i] = sorted[i];
s[i] = s[i-1] + sorted[i];
}
printf("\n");
sort(sorted+1,sorted+1+n,cmp);
build(1,n,0);
int x,y,k,res;
while(m--)
{
scanf("%d%d%d",&x,&y,&k);
lsum = lnum = 0;
res = query(x,y,k,1,n,0);
printf("%d ",res); //输出第k大值
rnum = y-x+1 - lnum;
rsum = s[y] - s[x-1] - lsum - res;
printf("%lld %lld\n",lsum,rsum); //区间内比第k大值小的和大的值的和;
}
}
eg:
题目参考:http://blog.youkuaiyun.com/yang_7_46/article/details/8613461
POJ 2104 k-th number
http://poj.org/problem?id=2104
题意:
求区间内k小值;
思路:
直接划分树,求第k小值;
code:
#define N 123456
int sorted[N]={0};
int val[20][N]={0};
int num[20][N]={0};
int lnum, rnum;
long long sum[20][N]={0};
long long lsum, rsum;
void build(int l, int r, int d)
{
if (l == r) return ;
int mid = (l + r) >> 1;
int same = mid - l + 1;
for (int i=l; i<=r; i++)
if (val[d][i] < sorted[mid])
same--;
int lp = l, rp = mid+1;
for (int i=l; i<=r; i++) {
if (i == l) {
num[d][i] = 0;
sum[d][i] = 0;
} else {
num[d][i] = num[d][i-1];
sum[d][i] = sum[d][i-1];
}
if (val[d][i] < sorted[mid]) {
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else if (val[d][i] > sorted[mid])
val[d+1][rp++] = val[d][i];
else {
if (same) {
same--;
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else val[d+1][rp++] = val[d][i];
}
}
build(l, mid, d+1);
build(mid+1, r, d+1);
}
int query(int a, int b, int k, int l, int r, int d) {
if (a == b) return val[d][a];
int mid = (l + r) >> 1;
int s, ss;
long long sss;
if (a == l) {
s = num[d][b];
ss = 0;
sss = sum[d][b];
} else {
s = num[d][b] - num[d][a-1];
ss = num[d][a-1];
sss = sum[d][b] - sum[d][a-1];
}
if (s >= k) {
a = l + ss;
b = l + ss + s - 1;
return query(a, b, k, l, mid, d+1);
} else {
lnum += s;
lsum += sss;
a = mid+1 + a - l - ss;
b = mid+1 + b - l - num[d][b];
return query(a, b, k-s, mid+1, r, d+1);
}
}
bool cmp(int a,int b)
{ return a<b; }
void solve(int n,int m)
{
for(int i=1;i<=n;i++)
{
scanf("%d",&sorted[i]);
val[0][i] = sorted[i];
}
sort(sorted+1,sorted+1+n,cmp);
build(1,n,0);
int x,y,k,res;
while(m--)
{
scanf("%d%d%d",&x,&y,&k);
res = query(x,y,k,1,n,0);
printf("%d\n",res);
}
}
int main() {
int n, m;
#ifndef ONLINE_JUDGE
freopen("test.txt","r",stdin);
#endif
scanf("%d%d",&n,&m);
solve(n,m);
return 0;
}
HDU 3473 Minimum Sum
http://acm.hdu.edu.cn/showproblem.php?pid=3473
题意:
求区间内sum(x-xi)的最小值;x属于该区间;
思路:
找到区间内中位数即可,求区间内中位数,记录区间内大于和小于中位数的数的和,差值加减多余的中位数的值就是答案了;
code:
#define N 123456
int sorted[N]={0};
int val[20][N]={0};
int num[20][N]={0};
int lnum, rnum;
long long sum[20][N]={0};
long long lsum, rsum;
void build(int l, int r, int d)
{
if (l == r) return ;
int mid = (l + r) >> 1;
int same = mid - l + 1;
for (int i=l; i<=r; i++)
if (val[d][i] < sorted[mid])
same--;
int lp = l, rp = mid+1;
for (int i=l; i<=r; i++) {
if (i == l) {
num[d][i] = 0;
sum[d][i] = 0;
} else {
num[d][i] = num[d][i-1];
sum[d][i] = sum[d][i-1];
}
if (val[d][i] < sorted[mid]) {
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else if (val[d][i] > sorted[mid])
val[d+1][rp++] = val[d][i];
else {
if (same) {
same--;
num[d][i]++;
sum[d][i] += val[d][i];
val[d+1][lp++] = val[d][i];
} else val[d+1][rp++] = val[d][i];
}
}
build(l, mid, d+1);
build(mid+1, r, d+1);
}
int query(int a, int b, int k, int l, int r, int d)
{
if (a == b)
return val[d][a];
int mid = (l + r) >> 1;
int s, ss;
long long sss;
if (a == l)
{
s = num[d][b];
ss = 0;
sss = sum[d][b];
}
else
{
s = num[d][b] - num[d][a-1];
ss = num[d][a-1];
sss = sum[d][b] - sum[d][a-1];
}
if (s >= k)
{
a = l + ss;
b = l + ss + s - 1;
return query(a, b, k, l, mid, d+1);
}
else
{
lnum += s;
lsum += sss;
a = mid+1 + a - l - ss;
b = mid+1 + b - l - num[d][b];
return query(a, b, k-s, mid+1, r, d+1);
}
}
bool cmp(int a,int b)
{ return a<b; }
void solve(int n,int m)
{
long long s[N]={0};
for(int i=1;i<=n;i++)
{
scanf("%d",&sorted[i]);
val[0][i] = sorted[i];
s[i] = s[i-1] + sorted[i];
}
scanf("%d",&m);
sort(sorted+1,sorted+1+n,cmp);
build(1,n,0);
int x,y,k,res;
while(m--)
{
scanf("%d%d",&x,&y);
x++,y++;
k = (y-x+2)>>1;
lsum = lnum = 0;
res = query(x,y,k,1,n,0);
rnum = y-x+1 - lnum;
rsum = s[y] - s[x-1] - lsum;
printf("%lld\n",(long long)(rsum-lsum+(lnum-rnum)*res)); //区间内比第k大值小的和大的值的和;
}
printf("\n");
}
int main()
{
int k,kk,n,m;
#ifndef ONLINE_JUDGE
freopen("test.txt","r",stdin);
#endif
scanf("%d",&k);
kk=0;
while(k--)
{
printf("Case #%d:\n",++kk);
scanf("%d",&n);
solve(n,0);
}
return 0;
}