hdu 3473 Minimum Sum

传送门

之前看挑战的时候看到一道分桶法的题目,其实我不是很明白分桶法应该怎么写。看到poj后面的讨论版上写着划分树裸题,而我以前就听说过了划分树,就干脆拿来学习一下。在写这篇博客的时候,其实我还是对这个东西不是很明白。在此先mark一下,以便日后再次学习。

划分树是利用模拟快速排序的方法,以树状结构保存信息的数据结构。作用是快速查找区间内第k大的值,我目前学习下来,应该是不能在线操作的。至于划分树怎么写,各位大能菊苣非常多,先贴上一个传送门。

别人的划分树链接

hdu这道题也算是划分树的裸题了吧,就是需要在查询的时候同时保存一下小于等于中位数的数的个数以及其和,比poj的板子题稍微强一点。我也是参考了别人的代码才能够通过的。

附上poj板子题

关于这个划分树模板,我是根据百度百科上的模板修改的,感觉原作者写得挺好的。

/*****************************************************/
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define   offcin        ios::sync_with_stdio(false)
#define   sigma_size    26
#define   lson          l,m,v<<1
#define   rson          m+1,r,v<<1|1
#define   slch          v<<1
#define   srch          v<<1|1
#define   sgetmid       int m = (l+r)>>1
#define   LL            long long
#define   ull           unsigned long long
#define   mem(x,v)      memset(x,v,sizeof(x))
#define   lowbit(x)     (x&-x)
#define   bits(a)       __builtin_popcount(a)
#define   mk            make_pair
#define   pb            push_back
#define   fi            first
#define   se            second

const int    INF    = 0x3f3f3f3f;
const LL     INFF   = 1e18;
const double pi     = acos(-1.0);
const double inf    = 1e18;
const double eps    = 1e-9;
const LL     mod    = 1e9+7;
const int    maxmat = 10;
const ull    BASE   = 31;

/*****************************************************/

const int maxn = 1e5 + 5;
int a[maxn], s[maxn];
int seg[20][maxn], num[20][maxn];
LL sum[20][maxn];
LL Sum[maxn];
int N, M;
void build(int l, int r, int v) {
    sgetmid;
    int lm = m - l + 1, lp = l, rp = m + 1;
    for (int i = l; i <= m; i ++) lm -= s[i] < s[m];
    for (int i = l; i <= r; i ++) {
        num[v][i] = i == l ? 0 : num[v][i - 1];
        sum[v][i] = i == l ? 0 : sum[v][i - 1];
        if (seg[v][i] == s[m]) {
            if (lm) {
                lm --;
                num[v][i] ++;
                sum[v][i] += seg[v][i];
                seg[v + 1][lp ++] = seg[v][i];
            }
            else seg[v + 1][rp ++] = seg[v][i];
        }
        else if (seg[v][i] < s[m]) {
            num[v][i] ++;
            sum[v][i] += seg[v][i];
            seg[v + 1][lp ++] = seg[v][i];
        }
        else seg[v + 1][rp ++] = seg[v][i];
    }
    if (l < r) {
        build(l, m, v + 1);
        build(m + 1, r, v + 1);
    }
}
LL suml, numl;
int query(int L, int R, int x, int l, int r, int v) {
    if (l == r) return seg[v][l];
    int s, ss; sgetmid;
    LL tmp = 0;
    if (l == L) s = 0, ss = num[v][R], tmp = sum[v][R];
    else s = num[v][L - 1], ss = num[v][R] - s, tmp = sum[v][R] - sum[v][L - 1];
    if (x <= ss) return query(l + s, l + s + ss - 1, x, l, m, v + 1);
    else {
        suml += tmp;
        numl += ss;
        return query(m + L - l - s + 1, m + R - l - s - ss + 1, x - ss, m + 1, r, v + 1);
    }
}
LL getans(int l, int r) {
    suml = numl = 0;
    int k = query(l, r, (r - l) / 2 + 1, 1, N, 0);
    LL ans = (LL)k * numl - suml;
    suml = Sum[r] - Sum[l - 1] - suml;
    numl = r - l + 1 - numl;
    ans += suml - numl * (LL)k;
    return ans; 
}
int main(int argc, char const *argv[]) {
    int T, kase = 1;
    cin>>T;
    while (T --) {
        mem(Sum, 0);
        scanf("%d", &N);
        for (int i = 1; i <= N; i ++) {
            scanf("%d", a + i);
            s[i] = seg[0][i] = a[i];
            Sum[i] = Sum[i - 1] + a[i];
        }
        sort(s + 1, s + N + 1);
        build(1, N, 0);
        scanf("%d", &M);
        printf("Case #%d:\n", kase ++);
        while (M --) {
            int l, r;
            scanf("%d%d", &l, &r); l ++, r ++;
            printf("%I64d\n", getans(l, r));
        }
        puts("");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值