【线段树】线段树及其相关 复习

划分树:

poj2104 K-th number

/*******************************\
 * @prob: poj2104 K-th number  *
 * @auth: Wang Junji           *
 * @stat: Accepted.            *
 * @date: June. 21st, 2012     *
 * @memo: 划分树                *
\*******************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 100010;
int tr[20][maxN], cntL[20][maxN], ord[maxN], num[maxN], n, m;

inline bool cmp(const int& a, const int& b) {return num[a] < num[b];}

void Build(int L, int R, int D)
{
    int Mid = (L + R) >> 1, p = L;
    for (int i = L; i < R; cntL[D][++i] = p)
        tr[D + 1][tr[D][i] < Mid ? p++ : Mid + i - p] = tr[D][i];
    if (p > L + 1) Build(L, Mid, D + 1); if (Mid < R - 1) Build(Mid, R, D + 1);
    return;
} /* Build */

int query(int l, int r, int L, int R, int D, int k)
{
    if (l + 1 == r) return num[ord[tr[D][l]]];
    int Mid = (l + r) >> 1, _L = L > l ? cntL[D][L] : l, _R = cntL[D][R];
    if (_R - _L >= k) return query(l, Mid, _L, _R, D + 1, k);
    else return query(Mid, r, Mid + L - _L, Mid + R - _R, D + 1, k - _R + _L);
} /* query */

int main()
{
    freopen("K_th.in", "r", stdin);
    freopen("K_th.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; ++i) scanf("%d", num + (ord[i] = i));
    std::sort(ord, ord + n, cmp);
    for (int i = 0; i < n; ++i) tr[0][ord[i]] = i;
    Build(0, n, 0);
    while (m--)
    {
        int L, R, k; scanf("%d%d%d", &L, &R, &k);
        printf("%d\n", query(0, n, --L, R, 0, k));
    } /* while */
    return 0;
} /* main */

/*

划分树入门题,不多说。
将代码缩了又缩,用了零下标、左闭右开区间表示法、以及cntL数组的绝对位置,使得划分树写起来更为流畅。

*/
hdu3473 Mininum Sum

/*******************************\
 * @prob: hdu3473 Minimum Sum  *
 * @auth: Wang Junji           *
 * @stat: Accepted.            *
 * @date: June. 21st, 2012     *
 * @memo: 划分树                *
\*******************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN =  100010;
typedef long long int64;
int64 sumL[20][maxN], sum[maxN];
int tr[20][maxN], cntL[20][maxN], num[maxN], ord[maxN], n, m, T;

inline bool cmp(const int& a, const int& b) {return num[a] < num[b];}

void Build(int L, int R, int D)
{
    int Mid = (L + R) >> 1, p = L; int64 tmp_sum = 0;
    for (int i = L; i < R; cntL[D][++i] = p, sumL[D][i] = tmp_sum)
        if (tr[D][i] < Mid) tr[D + 1][p++] = tr[D][i], tmp_sum += num[ord[tr[D][i]]];
        else tr[D + 1][Mid + i - p] = tr[D][i];
    if (p > L + 1) Build(L, Mid, D + 1); if (Mid < R - 1) Build(Mid, R, D + 1);
    return;
} /* Build */

int64 query(int l, int r, int L, int R, int D, int k, bool flag)
{
    if (l + 1 == r) return num[ord[tr[D][l]]] * (flag ? 1 : 2);
    int Mid = (l + r) >> 1, _L = L > l ? cntL[D][L] : l, _R = cntL[D][R];
    if (_R - _L >= k) return query(l, Mid, _L, _R, D + 1, k, flag);
    else return
        query(Mid, r, Mid + L - _L,
            Mid + R - _R, D + 1, k - _R + _L, flag)
            + ((sumL[D][R] - (L > l ? sumL[D][L] : 0)) << 1);
} /* query */

int main()
{
    freopen("sum.in", "r", stdin);
    freopen("sum.out", "w", stdout);
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d", &n);
        for (int i = 0; i < n; ++i) scanf("%d", num + (ord[i] = i));
        for (int i = 0; i < n; ++i) sum[i + 1] = sum[i] + num[i];
        std::stable_sort(ord, ord + n, cmp);
        for (int i = 0; i < n; ++i) tr[0][ord[i]] = i;
        Build(0, n, 0); scanf("%d", &m);
        static int Case = 0; printf("Case #%d:\n", ++Case);
        while (m--)
        {
            int L, R; scanf("%d%d", &L, &R); ++R;
            int64 tmp = query(0, n, L, R, 0, (R - L + 1) >> 1, (R - L) & 1);
            printf("%lld\n", sum[R] - sum[L] - tmp);
        } /* while */
        puts("");
    } /* while */
    return 0;
} /* main */

/*

首先,题目中所说的x一定是该区间的中位数。
然后看一个推理:
(见下图)
那么问题的关键就是如何求一个区间中前K大元素的和。
我们利用求区间第K大的思想,预先保存一个sumL[D][i],表示第D层,区间[L, i)的和。(L表示建树的时候当前的左界。)
于是,此问题就可以顺利解决了。

*/


面积树:

poj1656 Conting Black

/**********************************\
 * @prob: poj1656 Counting Black  *
 * @auth: Wang Junji              *
 * @stat: Accepted.               *
 * @date: June. 20th, 2012        *
 * @memo: 面积树                   *
\**********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 100010;

class SegTree
{
private:
    struct Node
    {
        int L1, R1, L2, R2, sum, sz, col; Node *luc, *ldc, *ruc, *rdc;
        Node(): sum(0), col(0) {} /* Node */
        Node(int L1, int R1, int L2, int R2):
            L1(L1), R1(R1), L2(L2), R2(R2), sum(0), col(0)
        {sz = (R1 - L1 + 1) * (R2 - L2 + 1);} /* Node */
    } NIL[maxN], *tot, *T;
    Node* NewNode(int L1, int R1, int L2, int R2)
    {
        Node* T = new (++tot) Node(L1, R1, L2, R2);
        T -> luc = T -> ldc = T -> ruc = T -> rdc = NIL;
        return T;
    } /* NewNode */
    void update(Node* T)
    {
        T -> sum = T -> luc -> sum + T -> ldc -> sum + T -> ruc -> sum + T -> rdc -> sum;
        T -> col = 0;
        if (T -> luc != NIL && T -> luc -> col != T -> col) T -> col = -1;
        if (T -> ldc != NIL && T -> ldc -> col != T -> col) T -> col = -1;
        if (T -> ruc != NIL && T -> ruc -> col != T -> col) T -> col = -1;
        if (T -> rdc != NIL && T -> rdc -> col != T -> col) T -> col = -1;
        return;
    } /* update */
    void passdown(Node* T)
    {
        if (T -> luc != NIL)
            T -> luc -> col = T -> col,
            T -> luc -> sum = T -> luc -> sz * T -> col;
        if (T -> ldc != NIL)
            T -> ldc -> col = T -> col,
            T -> ldc -> sum = T -> ldc -> sz * T -> col;
        if (T -> ruc != NIL)
            T -> ruc -> col = T -> col,
            T -> ruc -> sum = T -> ruc -> sz * T -> col;
        if (T -> rdc != NIL)
            T -> rdc -> col = T -> col,
            T -> rdc -> sum = T -> rdc -> sz * T -> col;
        T -> col = -1; return;
    } /* passdown */
    void chg(Node* T, int L1, int R1, int L2, int R2, int col)
    {
        if (T == NIL) return;
        if (L1 <= T -> L1 && R1 >= T -> R1 && L2 <= T -> L2 && R2 >= T -> R2)
        {T -> col = col, T -> sum = T -> sz * col; return;} /* if */
        if (T -> col >= 0) passdown(T);
        int Mid1 = (T -> L1 + T -> R1) >> 1, Mid2 = (T -> L2 + T -> R2) >> 1;
        if (L1 <= Mid1 && L2 <= Mid2) chg(T -> luc, L1, R1, L2, R2, col);
        if (L1 <= Mid1 && R2 > Mid2) chg(T -> ldc, L1, R1, L2, R2, col);
        if (R1 > Mid1 && L2 <= Mid2) chg(T -> ruc, L1, R1, L2, R2, col);
        if (R1 > Mid1 && R2 > Mid2) chg(T -> rdc, L1, R1, L2, R2, col);
        update(T); return;
    } /* chg */
    int query(Node* T, int L1, int R1, int L2, int R2)
    {
        if (T == NIL) return 0;
        if (L1 <= T -> L1 && R1 >= T -> R1 && L2 <= T -> L2 && R2 >= T -> R2)
            return T -> sum;
        if (T -> col >= 0) passdown(T);
        int Mid1 = (T -> L1 + T -> R1) >> 1, Mid2 = (T -> L2 + T -> R2) >> 1;
        int v1 = L1 <= Mid1 && L2 <= Mid2 ? query(T -> luc, L1, R1, L2, R2) : 0,
            v2 = L1 <= Mid1 && R2 > Mid2 ? query(T -> ldc, L1, R1, L2, R2) : 0,
            v3 = R1 > Mid1 && L2 <= Mid2 ? query(T -> ruc, L1, R1, L2, R2) : 0,
            v4 = R1 > Mid1 && R2 > Mid2 ? query(T -> rdc, L1, R1, L2, R2) : 0;
        return v1 + v2 + v3 + v4;
    } /* query */
    void Build(Node*& T, int L1, int R1, int L2, int R2)
    {
        T = NewNode(L1, R1, L2, R2);
        if (L1 == R1 && L2 == R2) return;
        int Mid1 = (L1 + R1) >> 1, Mid2 = (L2 + R2) >> 1;
        if (L1 <= Mid1 && L2 <= Mid2) Build(T -> luc, L1, Mid1, L2, Mid2);
        if (L1 <= Mid1 && R2 > Mid2) Build(T -> ldc, L1, Mid1, Mid2 + 1, R2);
        if (R1 > Mid1 && L2 <= Mid2) Build(T -> ruc, Mid1 + 1, R1, L2, Mid2);
        if (R1 > Mid1 && R2 > Mid2) Build(T -> rdc, Mid1 + 1, R1, Mid2 + 1, R2);
        update(T); return;
    } /* Build */
public:
    SegTree() {T = tot = NIL;} /* SegTree */
    void Build(int L1, int R1, int L2, int R2)
    {Build(T, L1, R1, L2, R2); return;} /* Build */
    void chg(int L1, int R1, int L2, int R2, int col)
    {chg(T, L1, R1, L2, R2, col); return;} /* chg */
    int query(int L1, int R1, int L2, int R2)
    {return query(T, L1, R1, L2, R2);} /* query */
} Tr;

int main()
{
    freopen("black.in", "r", stdin);
    freopen("black.out", "w", stdout);
    Tr.Build(1, 100, 1, 100);
    int m = 0; scanf("%d", &m);
    while (m--)
    {
        static char str[20]; int x, y, L; scanf("%s%d%d%d", str, &x, &y, &L);
        if (!strcmp(str, "TEST")) printf("%d\n", Tr.query(x, x + L - 1, y, y + L - 1));
        else if (!strcmp(str, "BLACK")) Tr.chg(x, x + L - 1, y, y + L - 1, 1);
        else if (!strcmp(str, "WHITE")) Tr.chg(x, x + L - 1, y, y + L - 1, 0);
    } /* while */
    return 0;
} /* main */

/*

二维线段树入门。
一开始把WHITE打成WHILE了……

*/
hdu1823 Luck and Love

/*********************************\
 * @prob: hdu1823 Luck and Love  *
 * @auth: Wang Junji             *
 * @stat: Accepted.              *
 * @date: June. 20th, 2012       *
 * @memo: 面积树                  *
\*********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 1000010;
const double INF = 1e18, zero = 1e-10;

template <typename _Tp>
inline _Tp& gmax(_Tp& a, const _Tp& b) {return a > b ? a : (a = b);} /* gmax */

class SegTree
{
private:
    int L1[maxN], R1[maxN], L2[maxN], R2[maxN], tot;
    int luc[maxN], ruc[maxN], ldc[maxN], rdc[maxN];
    double Max[maxN]; bool clr[maxN];
    void update(int p)
    {
        if (luc[p]) gmax(Max[p], Max[luc[p]]);
        if (ruc[p]) gmax(Max[p], Max[ruc[p]]);
        if (ldc[p]) gmax(Max[p], Max[ldc[p]]);
        if (rdc[p]) gmax(Max[p], Max[rdc[p]]);
        return;
    } /* update */
    void passdown(int p)
    {
        if (luc[p]) clr[luc[p]] = 1, Max[luc[p]] = -INF;
        if (ruc[p]) clr[ruc[p]] = 1, Max[ruc[p]] = -INF;
        if (ldc[p]) clr[ldc[p]] = 1, Max[ldc[p]] = -INF;
        if (rdc[p]) clr[rdc[p]] = 1, Max[rdc[p]] = -INF;
        clr[p] = 0; return;
    } /* passdown */
    void Ins(int p, int x, int y, double val)
    {
        if (L1[p] == R1[p] && L1[p] == x && L2[p] == R2[p] && L2[p] == y)
        {gmax(Max[p], val); return;} /* if */
        if (clr[p]) passdown(p);
        int Mid1 = (L1[p] + R1[p]) >> 1, Mid2 = (L2[p] + R2[p]) >> 1;
        if (x <= Mid1 && y <= Mid2) Ins(luc[p], x, y, val);
        else if (x <= Mid1 && y > Mid2) Ins(ldc[p], x, y, val);
        else if (x > Mid1 && y <= Mid2) Ins(ruc[p], x, y, val);
        else if (x > Mid1 && y > Mid2) Ins(rdc[p], x, y, val);
        update(p); return;
    } /* Ins */
    double query(int p, int l1, int r1, int l2, int r2)
    {
        if (!p) return -INF;
        if (l1 <= L1[p] && r1 >= R1[p] && l2 <= L2[p] && r2 >= R2[p]) return Max[p];
        if (clr[p]) passdown(p);
        int Mid1 = (L1[p] + R1[p]) >> 1, Mid2 = (L2[p] + R2[p]) >> 1;
        double v1 = l1 <= Mid1 && l2 <= Mid2 ? query(luc[p], l1, r1, l2, r2) : -INF,
               v2 = l1 <= Mid1 && r2 > Mid2 ? query(ldc[p], l1, r1, l2, r2) : -INF,
               v3 = r1 > Mid1 && l2 <= Mid2 ? query(ruc[p], l1, r1, l2, r2) : -INF,
               v4 = r1 > Mid1 && r2 > Mid2 ? query(rdc[p], l1, r1, l2, r2) : -INF;
        return std::max(std::max(v1, v2), std::max(v3, v4));
    } /* query */
public:
    SegTree(): tot(0)
    {
        Max[0] = -INF;
        memset(luc, 0, sizeof luc);
        memset(ruc, 0, sizeof ruc);
        memset(ldc, 0, sizeof ldc);
        memset(rdc, 0, sizeof rdc);
    } /* SegTree */
    void Build(int l1, int r1, int l2, int r2)
    {
        int p = ++tot; L1[p] = l1, R1[p] = r1, L2[p] = l2, R2[p] = r2;
        if (l1 == r1 && l2 == r2) {Max[p] = -INF; return;}
        int Mid1 = (l1 + r1) >> 1, Mid2 = (l2 + r2) >> 1;
        if (l1 <= Mid1 && l2 <= Mid2) luc[p] = tot + 1, Build(l1, Mid1, l2, Mid2);
        if (l1 <= Mid1 && r2 > Mid2) ldc[p] = tot + 1, Build(l1, Mid1, Mid2 + 1, r2);
        if (r1 > Mid1 && l2 <= Mid2) ruc[p] = tot + 1, Build(Mid1 + 1, r1, l2, Mid2);
        if (r1 > Mid1 && r2 > Mid2) rdc[p] = tot + 1, Build(Mid1 + 1, r1, Mid2 + 1, r2);
        update(p); return;
    } /* Build */
    void Ins(int x, int y, double val) {Ins(1, x, y, val); return;} /* Ins */
    double query(int l1, int r1, int l2, int r2) 
    {return query(1, l1, r1, l2, r2);} /* query */
    void clear() {Max[1] = -INF; clr[1] = 1; return;} /* clear */
} Tr; int m;

int main()
{
    freopen("love.in", "r", stdin);
    freopen("love.out", "w", stdout);
    Tr.Build(100, 200, 0, 1000);
    while (scanf("%d", &m) != EOF && m)
    {
        Tr.clear();
        double tmp; int L1, R1, L2, R2;
        while (m--) switch (scanf("\n"), getchar())
        {
        case 'I':
            scanf("%d", &L1);
            scanf("%lf", &tmp);
            L2 = int(tmp * 10);
            scanf("%lf", &tmp);
            Tr.Ins(L1, L2, tmp);
            break;
        case 'Q':
            scanf("%d%d", &L1, &R1);
            scanf("%lf", &tmp); L2 = int(tmp * 10);
            scanf("%lf", &tmp); R2 = int(tmp * 10);
            if (L1 > R1) std::swap(L1, R1);
            if (L2 > R2) std::swap(L2, R2);
            tmp = Tr.query(L1, R1, L2, R2);
            if (tmp + INF < zero) printf("%d\n", -1);
            else printf("%.1lf\n", tmp);
            break;
        } /* switch */
    } /* while */
    return 0;
} /* main */

/*

面积树入门,不再多说。

不知道为啥G++WA而C++AC……

*/
简单线段树:

hdu1698 Just a Hook

/*******************************\
 * @prob: hdu1698 Just a Hook  *
 * @auth: Wang Junji           *
 * @stat: Accepted.            *
 * @date: June. 21st, 2012     *
 * @memo: 线段树                *
\*******************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#define lc p << 1, l, Mid
#define rc p << 1 | 1, Mid, r

const int maxN = 400010;
int sum[maxN], col[maxN], n, m, T;

inline void update(int p)
{
    sum[p] = sum[p << 1] + sum[p << 1 | 1]; 
    col[p] = col[p << 1] == col[p << 1 | 1] ? col[p << 1] : 0;
    return;
} /* update */

void Build(int p, int l, int r)
{
    if (l + 1 == r) {col[p] = sum[p] = 1; return;} /* if */
    int Mid = (l + r) >> 1; Build(lc), Build(rc);
    update(p); return;
} /* Build */

void chg(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && R >= r) {sum[p] = (col[p] = c) * (r - l); return;} /* if */
    int Mid = (l + r) >> 1;
    if (col[p])
    {
        col[p << 1] = col[p << 1 | 1] = col[p];
        sum[p << 1] = (Mid - l) * col[p];
        sum[p << 1 | 1] = (r - Mid) * col[p];
        col[p] = 0;
    } /* if */
    if (L < Mid) chg(lc, L, R, c); if (Mid < R) chg(rc, L, R, c);
    update(p); return;
} /* chg */

int main()
{
    freopen("hook.in", "r", stdin);
    freopen("hook.out", "w", stdout);
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d", &n, &m); Build(1, 0, n);
        while (m--)
        {
            int L, R, c; scanf("%d%d%d", &L, &R, &c);
            chg(1, 0, n, --L, R, c);
        } /* while */
        static int Case = 0;
        printf("Case %d: The total value of the hook is %d.\n", ++Case, sum[1]);
    } /* while */
    return 0;
} /* main */

/*

线段树的入门题,不再多说。

*/

线段树与其它结合:

hdu4027 Can you answer these queries

/*************************************************\
 * @prob: hdu4027 Can you answer these queries?  *
 * @auth: Wang Junji        * @stat: Accepted.   *
 * @date: June. 22nd, 2012  * @memo: 线段树       *
\*************************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>

#define lc (p << 1    )
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l  , Mid
#define RC rc, Mid, r

const int maxN = 500010;
typedef long long int64;
int64 sum[maxN], val[maxN];
bool flag[maxN];
int n, m, T;

template <typename _Tp>
inline _Tp& gmax(_Tp& a, const _Tp& b)
{return a > b ? a : (a = b);}
/* gmax */

template <typename _Tp>
inline _Tp& gmin(_Tp& a, const _Tp& b)
{return a < b ? a : (a = b);}
/* gmin */

inline void update(int p)
{
    sum [p] = sum [lc] +  sum [rc];
    flag[p] = flag[lc] && flag[rc];
    return;
} /* update */

void Build(int p, int l, int r)
{
    if (r - l == 1)
    {
        sum [p] = val[l];
        flag[p] = sum[p] <= 1LL;
        return;
    } /* if */
    Build(LC); Build(RC); update(p); return;
} /* Build */

void attack(int p, int l, int r, int L, int R)
{
    if (flag[p]) return;
    if (r - l == 1)
    {
        sum [p] = (int64)sqrt((double)sum[p]);
        flag[p] = sum[p] <= 1LL;
        return;
    } /* if */
    if (L   < Mid) attack(LC, L, R);
    if (Mid < R  ) attack(RC, L, R);
    update(p); return;
} /* attack */

int64 query(int p, int l, int r, int L, int R)
{
    if (L <= l && R >= r) return sum[p];
    if (R   <= Mid) return query(LC, L, R);
    if (Mid <= L  ) return query(RC, L, R);
    return query(LC, L, R) + query(RC, L, R);
} /* query */

int main()
{
    freopen("query.in" , "r", stdin );
    freopen("query.out", "w", stdout);
    while (~scanf("%d", &n))
    {
        for (int i = 0; i < n; ++i) scanf("%lld", val + i);
        Build(1, 0, n);
        scanf("%d", &m);
        static int Case = 0, L, R, opt;
        printf("Case #%d:\n", ++Case);
        while (m-- && ~scanf("%d%d%d", &opt, &L, &R))
        {
            if (L > R) std::swap(L, R);
            gmax(L, 1); gmin(R, n); --L;
            if (opt) printf("%lld\n", query (1, 0, n, L, R));
            else                      attack(1, 0, n, L, R);
        } /* while */
        puts("");
    } /* while */
    return 0;
} /* main */

/*

一看到这题就懵了,每次开平方,要命啊……

然后看了一下解题报告说64为整型以内的数开平方次数不超过7次都可以变成1,于是拿计算器试了一下果然如此。

代码很快就打出来了,然后就是TLE,WA交替……

途中推倒重来2次,对着3篇代码狂改终于AC……

那么只需要记录一下各个区间是否已经完全变成1,若是,则不需要对其进行更新。

此时的心情唯有一句能够概括:for (;;) puts("fuck!!!");

*/

hdu3954 Level up

/****************************\
 * @prob: hdu3954 Level up  *
 * @auth: Wang Junji        *
 * @stat: Accepted.         *
 * @date: June. 22nd, 2012  *
 * @memo: 线段树             *
\****************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>
#define lc (p << 1    )
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l  , Mid
#define RC rc, Mid, r

const int maxN = 100010, INF = 0x3f3f3f3f;
typedef int arr[maxN];
arr max_exp, min_dis, max_lev, lazy, need;
int n, m, K, T;

inline void pushdown(int p) //标记下载。
{
    max_exp[lc] += max_lev[lc] * lazy[p], max_exp[rc] += max_lev[rc] * lazy[p];
    //这里更新经验是用左右子树各自的等级,而不是父亲的等级。
    min_dis[lc] -=               lazy[p], min_dis[rc] -=               lazy[p];
    lazy   [lc] +=               lazy[p], lazy   [rc] +=               lazy[p];
    lazy   [p ]  = 0;
} /* pushdown */

inline void update(int p) //标记上传。
{
    max_exp[p] = std::max(max_exp[lc], max_exp[rc]);
    min_dis[p] = std::min(min_dis[lc], min_dis[rc]);
    max_lev[p] = std::max(max_lev[lc], max_lev[rc]);
} /* update */

void Build(int p, int l, int r) //建树初始化。
{
    max_lev[p] = 1;
    max_exp[p] = 0;
    lazy   [p] = 0;
    min_dis[p] = need[1];
    if (r - l == 1) return;
    else Build(LC), Build(RC);
} /* Build */

void change(int p, int l, int r, int L, int R, int val)
{
    if (r - l == 1) //已经到达叶结点,则直接更新等级信息。
    {
        max_exp[p] += max_lev[p] * val;
        while (max_exp[p] >= need[max_lev[p]]) ++max_lev[p];
        min_dis[p] =  (need[max_lev[p]] - max_exp[p]) / max_lev[p]
                   + ((need[max_lev[p]] - max_exp[p]) % max_lev[p] != 0);
        return;
    } /* if */
    if (L <= l && R >= r)
    {
        if (val >= min_dis[p]) //区间内有英雄要升级。
        {
            pushdown(p);
            change(LC, L, R, val);
            change(RC, L, R, val);
            update  (p);
        } /* if */
        else //否则累加懒标记。
        {
            max_exp[p] += max_lev[p] * val;
            min_dis[p] -=              val;
            lazy   [p] +=              val;
        } /* else */
        return;
    } /* if */
    if (lazy[p]) pushdown(p);
    if (L   < Mid) change(LC, L, R, val);
    if (Mid < R  ) change(RC, L, R, val);
    update(p);
} /* change */

int query(int p, int l, int r, int L, int R)
{
    if (L <= l && R >= r) return max_exp[p];
    if (lazy[p]   ) pushdown(p); //记得传标记。
    if (R   <= Mid) return query(LC, L, R);
    if (Mid <= L  ) return query(RC, L, R);
    return std::max(query(LC, L, R), query(RC, L, R));
} /* query */

int main()
{
    freopen("level.in",  "r", stdin );
    freopen("level.out", "w", stdout);
    for (scanf("%d", &T); T--; puts(""))
    {
        scanf("%d%d%d", &n, &K, &m);
        need[0] = 0;
        for (int i = 1; i < K; ++i)
            scanf("%d", need + i);
        need[K] = INF;
        Build(1, 0, n);
        static int Case = 0, L, R, val;
        static char opt[10];
        printf("Case %d:\n", ++Case);
        while (m--) switch (scanf("%s%d%d", opt, &L, &R), opt[0])
        {
        case 'W': scanf ("%d", &val); change(1, 0, n, --L, R, val); break;
        case 'Q': printf("%d\n",      query (1, 0, n, --L, R    )); break;
        } /* switch */
    } /* for */
    return 0;
} /* main */

/*

用线段树维护每个区间的最大经验,最大等级,和最小升级经验基数(升级所需经验与等级的比值)。

当有一个区间的英雄可以升级时,将懒标记释放。

*/
hdu3016 Man Down

/**********************************\
 * @prob: hdu3016 Man Down        *
 * @auth: Wang Junji              *
 * @stat: Accepted.               *
 * @date: June. 22nd, 2012        *
 * @memo: 线段树预处理、动态规划     *
\**********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1    )
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l  , Mid
#define RC rc, Mid, r

const int maxN = 500010;
struct Plank
{
    int H, L, R, val; Plank() {} /* Plank */
    Plank(int H, int L, int R, int val): H(H), L(L), R(R), val(val) {} /* Plank */
} plank[maxN];
typedef int arr[maxN];
arr col, left, right, f;
int n, Lim;

inline int& gmax(int& a, const int& b) {return a > b ? a : (a = b);} /* gmax */

inline bool cmp(const Plank& a, const Plank& b) {return a.H < b.H;} /* cmp */

void Build(int p, int l, int r)
{
    col[p] = 0;
    if (r - l == 1) return;
    Build(LC); Build(RC); return;
} /* Build */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && R >= r) {col[p] = c; return;} /* if */
    if (col[p] >= 0) col[lc] = col[rc] = col[p], col[p] = -1;
    if (L   < Mid) change(LC, L, R, c);
    if (Mid < R  ) change(RC, L, R, c);
    col[p] = col[lc] == col[rc] ? col[lc] : -1;
    return;
} /* change */

int query(int p, int l, int r, int x)
{
    if (col[p] >= 0) return col[p];
    if (x < Mid) return query(LC, x);
    else         return query(RC, x);
} /* query */

int main()
{
    freopen("man_down.in" , "r", stdin );
    freopen("man_down.out", "w", stdout);
    while (~scanf("%d", &n))
    {
        for (int i = 1, H, L, R, val; i < n + 1; ++i)
            scanf("%d%d%d%d", &H, &L, &R, &val),
            plank[i] = Plank(H, --L, R, val), gmax(Lim, R);
        std::sort(plank + 1, plank + n + 1, cmp);
        Build(1, 0, ++Lim);
        for (int i = 1; i < n + 1; ++i)
        {
            left [i] = query(1, 0, Lim, plank[i].L    );
            right[i] = query(1, 0, Lim, plank[i].R - 1);
            change(1, 0, Lim, plank[i].L, plank[i].R, i);
        } /* for */
        memset(f, 0, sizeof f); f[n] = 100 + plank[n].val;
        for (int i = n; i; --i) if (f[i] > 0)
        {
            gmax(f[left [i]], f[i] + plank[left [i]].val);
            gmax(f[right[i]], f[i] + plank[right[i]].val);
        } /* if */
        printf("%d\n", f[0] > 0 ? f[0] : -1);
    } /* while */
    return 0;
} /* main */

/*

先用线段树快速预处理出每个跳板左边界和右边界的正下方的结点编号,然后直接动态规划即可。

*/
zoj3511 Cake Robbery

/********************************\
 * @prob: zoj3511 Cake Robbery  *
 * @auth: Wang Junji            *
 * @stat: Accepted.             *
 * @date: June. 22nd, 2012      *
 * @memo: 线段树                 *
\********************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1    )
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l  , Mid
#define RC rc, Mid, r

const int maxN = 100010;
struct Cut {int x, y;} cut[maxN];
int sum[maxN], n, m;

inline bool cmp(const Cut& a, const Cut& b) {return a.y - a.x < b.y - b.x;} /* cmp */
inline int& gmax(int& a, const int& b) {return a > b ? a : (a = b);} /* gmax */

void Build(int p, int l, int r)
{
    if ((sum[p] = r - l) == 1) return;
    Build(LC); Build(RC); return;
} /* Build */

void change(int p, int l, int r, int L, int R)
{
    if (L <= l && R >= r) {sum[p] = 0; return;}
    if (sum[p] == 0) return;
    if (L   < Mid) change(LC, L, R);
    if (Mid < R  ) change(RC, L, R);
    sum[p] = sum[lc] + sum[rc]; return;
} /* change */

int query(int p, int l, int r, int L, int R)
{
    if (L <= l && R >= r) return sum[p];
    if (R   <= Mid) return query(LC, L, R);
    if (Mid <= L  ) return query(RC, L, R);
    return query(LC, L, R) + query(RC, L, R);
} /* query */

int main()
{
    freopen("cake.in" , "r", stdin );
    freopen("cake.out", "w", stdout);
    while (~scanf("%d%d", &n, &m))
    {
        for (int i = 0; i < m; ++i)
        {
            scanf("%d%d", &cut[i].x, &cut[i].y);
            if (cut[i].x > cut[i].y)
                std::swap(cut[i].x, cut[i].y);
            --cut[i].x;
        } /* for */
        std::sort(cut, cut + m, cmp);
        Build(1, 0, n);
        int res = 0;
        for (int i = 0; i < m; ++i)
        {
            gmax(res, query(1, 0, n, cut[i].x    , cut[i].y    ));
            change         (1, 0, n, cut[i].x + 1, cut[i].y - 1);
        } /* for */
        gmax(res, query(1, 0, n, 0, n));
        printf("%d\n", res);
    } /* while */
    return 0;
} /* main */

/*

模拟切蛋糕的过程,从小块的开始切,每次将已经切掉的顶点从线段树中删掉,取所有当中的最大值即可。
根据多边形边数等于点数可以计算出每一次的边数。

*/

矩形面积并:

hdu1542 Atlantis

/**************************************************\
 * @prob: hdu1542 Atlantis                        *
 * @auth: Wang Junji                              *
 * @stat: Accepted.                               *
 * @date: June. 27th, 2012                        *
 * @memo: 线段树、扫描线、区间离散化、矩形面积并       *
\**************************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

const int maxN = 210, maxM = 400010;

struct Seg
{
    double H, L, R; int c; Seg() {} /* Seg */
    Seg(double H, double L, double R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN]; int col[maxM], n; double sum[maxM], tab[maxM];

inline bool cmp(const Seg& a, const Seg& b) {return a.H < b.H;} /* cmp */

inline void update(int p, int l, int r)
{
    if (col[p]) sum[p] = tab[r] - tab[l];
    else if (r - l == 1) sum[p] = 0;
    else sum[p] = sum[lc] + sum[rc];
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && R >= r) {col[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("Atlantis.in" , "r", stdin );
    freopen("Atlantis.out", "w", stdout);
    while (~scanf("%d", &n) && n)
    {
        int cnt_s = 0, cnt_t = 0;
        while (n--)
        {
            double lwr, upr, L, R;
            scanf("%lf%lf%lf%lf", &L, &lwr, &R, &upr);
            seg[cnt_s++] = Seg(lwr, L, R,  1);
            seg[cnt_s++] = Seg(upr, L, R, -1);
            tab[cnt_t++] = L;
            tab[cnt_t++] = R;
        } /* while */
        std::sort(tab, tab + cnt_t);
        std::sort(seg, seg + cnt_s, cmp);
        cnt_t = std::unique(tab, tab + cnt_t) - tab;
        memset(sum, 0, sizeof sum);
        memset(col, 0, sizeof col);
        double ans = 0;
        for (int i = 0; i + 1 < cnt_s; ++i)
        {
            int L = std::lower_bound(tab, tab + cnt_t, seg[i].L) - tab,
                R = std::lower_bound(tab, tab + cnt_t, seg[i].R) - tab;
            change(1, 0, cnt_t, L, R, seg[i].c);
            ans += sum[1] * (seg[i + 1].H - seg[i].H);
        } /* for */
        static int Case = 0;
        printf("Test case #%d\nTotal explored area: %.2lf\n\n", ++Case, ans);
    } /* while */
    return 0;
} /* main */

/*

对横轴离散化后建树,然后按纵轴从小到大依次进行扫描,用col值记录每个区间的下边界比上边界多几个,遇到一个矩形下边界将对应区间col值加一,遇到一个矩形上边界将对应区间col值减一,

*/

hdu3264 Posters

/******************************\
 * @prob: hdu3265 Posters     *
 * @auth: Wang Junji          *
 * @stat: Accepted.           *
 * @date: June. 27th, 2012    *
 * @memo: 矩形面积并、线段树     *
\******************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

const int maxN = 400010;
typedef long long int64;

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

struct Seg
{
    int H, L, R, c; Seg() {} /* Seg */
    Seg(int H, int L, int R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN]; int cnt[maxN], n; int64 sum[maxN];

inline bool cmp(const Seg& a, const Seg& b) {return a.H < b.H;} /* cmp */

inline void update(int p, int l, int r)
{
    if (cnt[p]) sum[p] = r - l;
    else if (r - l == 1) sum[p] = 0;
    else sum[p] = sum[lc] + sum[rc];
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && r <= R) {cnt[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("posters.in" , "r", stdin );
    freopen("posters.out", "w", stdout);
    while (~scanf("%d", &n) && n)
    {
        int cnt_s = 0, max_R = 0;
        while (n--)
        {
            int x1, y1, x2, y2, x3, y3, x4, y4;
            scanf("%d%d%d%d%d%d%d%d", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);
            max_R = std::max(max_R, x2);
            seg[cnt_s++] = Seg(y1, x1, x3,  1);
            seg[cnt_s++] = Seg(y1, x3, x4,  1);
            seg[cnt_s++] = Seg(y1, x4, x2,  1);
            seg[cnt_s++] = Seg(y2, x1, x3, -1);
            seg[cnt_s++] = Seg(y2, x3, x4, -1);
            seg[cnt_s++] = Seg(y2, x4, x2, -1);
            seg[cnt_s++] = Seg(y3, x3, x4, -1);
            seg[cnt_s++] = Seg(y4, x3, x4,  1);
        } /* while */
        std::sort(seg, seg + cnt_s, cmp);
        memset(cnt, 0, sizeof cnt);
        memset(sum, 0, sizeof sum);
        int64 ans = 0;
        for (int i = 0; i < cnt_s; ++i)
        {
            if (seg[i].L < seg[i].R) //一定要加次判断,否则出错。
                change(1, 0, max_R, seg[i].L, seg[i].R, seg[i].c);
            ans += sum[1] * (seg[i + 1].H - seg[i].H);
        } /* for */
        printf("%lld\n", ans);
    } /* while */
    return 0;
} /* main */

/*

矩形面积并。
既然矩形中有镂空,那就将矩形拆成四个小矩形,再用普通面积并的方法计算。

*/

hdu3642 Get the Treasury

/********************************************\
 * @prob: hdu3642 Get the Treasury          *
 * @auth: Wang Junji  * @stat: Accepted.    *
 * @date: June. 27th, 2012                  *
 * @memo: 长方体体积交、区间离散化、线段树       *
\********************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

typedef long long int64;
const int maxN = 10010; 

struct Seg
{
    int H, L, R, c; Seg() {} /* Seg */
    Seg(int H, int L, int R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN];
struct Point {int x0, y0, z0, x1, y1, z1;} p[maxN];

int cnt[maxN], tab_x[maxN], tab_z[maxN], cnt_x, cnt_z, n, T;
int sum[maxN], one[maxN], two[maxN];

inline bool cmp(const Seg& a, const Seg& b) {return a.H < b.H;} /* cmp */

inline void update(int p, int l, int r)
{
    if      (cnt[p] >  2) sum[p] = two[p] = one[p] = tab_x[r] - tab_x[l];
    else if (cnt[p] == 2)
    {
        two[p] = one[p] = tab_x[r] - tab_x[l];
        if (r - l == 1) sum[p] = 0;
        else sum[p] = one[lc] + one[rc];
        //该区间的覆盖次数已经为2,
        //那么若其子区间再被覆盖1次,
        //则该区间的覆盖次数可达3。
    } /* else */
    else if (cnt[p] == 1)
    {
        one[p] = tab_x[r] - tab_x[l];
        if (r - l == 1) sum[p] = two[p] = 0;
        else
            sum[p] = two[lc] + two[rc],
            //该区间的覆盖次数已经为1,
            //那么若其子区间再被覆盖2次,
            //则该区间的覆盖次数可达3。
            two[p] = one[lc] + one[rc];
            //该区间的覆盖次数已经为1,
            //那么若其子区间再被覆盖1次,
            //则该区间的覆盖次数可达2。
    } /* else */
    else if (r - l == 1) sum[p] = two[p] = one[p] = 0;
    else 
        sum[p] = sum[lc] + sum[rc],
        two[p] = two[lc] + two[rc],
        one[p] = one[lc] + one[rc];
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && r <= R) {cnt[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("Treasury.in" , "r", stdin );
    freopen("Treasury.out", "w", stdout);
    for (scanf("%d", &T); T--;)
    {
        scanf("%d", &n); cnt_z = 0;
        for (int i = 0; i < n; ++i)
        {
            scanf("%d%d%d%d%d%d",
                &p[i].x0, &p[i].y0, &p[i].z0,
                &p[i].x1, &p[i].y1, &p[i].z1);
            tab_z[cnt_z++] = p[i].z0, tab_z[cnt_z++] = p[i].z1;
            tab_x[cnt_x++] = p[i].x0, tab_x[cnt_x++] = p[i].x1;
        } /* for */
        std::sort(tab_z, tab_z + cnt_z);
        cnt_z = std::unique(tab_z, tab_z + cnt_z) - tab_z;
        std::sort(tab_x, tab_x + cnt_x);
        cnt_x = std::unique(tab_x, tab_x + cnt_x) - tab_x;
        for (int i = 0; i < n; ++i)
            p[i].x0 = std::lower_bound(tab_x, tab_x + cnt_x, p[i].x0) - tab_x,
            p[i].x1 = std::lower_bound(tab_x, tab_x + cnt_x, p[i].x1) - tab_x;
        int64 ans = 0;
        for (int i = 0; i + 1 < cnt_z; ++i)
        {
            int cnt_s = 0;
            for (int j = 0; j < n; ++j)
            if (p[j].z0 <= tab_z[i] && p[j].z1 >= tab_z[i + 1])
                seg[cnt_s++] = Seg(p[j].y0, p[j].x0, p[j].x1,  1),
                seg[cnt_s++] = Seg(p[j].y1, p[j].x0, p[j].x1, -1);
            std::sort(seg, seg + cnt_s, cmp);
            memset(cnt, 0, sizeof cnt);
            memset(sum, 0, sizeof sum);
            memset(one, 0, sizeof one);
            memset(two, 0, sizeof two);
            int64 ths = 0;
            for (int j = 0; j < cnt_s; ++j)
            {
                if (seg[j].L < seg[j].R)
                    change(1, 0, cnt_x, seg[j].L, seg[j].R, seg[j].c);
                ths += (int64)sum[1] * (seg[j + 1].H - seg[j].H);
            } /* for */
            ans += ths * (tab_z[i + 1] - tab_z[i]);
        } /* for */
        static int Case = 0;
        printf("Case %d: %lld\n", ++Case, ans);
    } /* for */
    return 0;
} /* main */

/*

求覆盖3次以上的长方体体积并。
现将竖坐标(z坐标)和横坐标(x坐标)离散化。
然后枚举z,在每一层z中求一次矩形面积交。
与面积并不同,这里需要记录的是one和two(即该区间中被覆盖1次和2次的长度),
更新的时候:
若该区间的覆盖次数大于2,one、two和sum都直接计算;
若该区间的覆盖次数等于2,one和two直接计算,并用左右子树的one来更新父亲的sum;
若该区间的覆盖次数等于1,one直接计算,并用one更新two,用two更新sum;
否则,用one更新one,用two更新two,用sum更新sum。

要用long long类型。

*/

hdu3255 Farming

/********************************************\
 * @prob: hdu3255 Farming                   *
 * @auth: Wang Junji   * @stat: Accepted.   *
 * @date: June. 27th, 2012                  *
 * @memo: 长方体体积并、线段树、区间离散化       *
\********************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

typedef long long int64;
const int maxN = 200010;

struct Node {int x0, x1, y0, y1, v;} node[maxN];

struct Seg
{
    int H, L, R, c; Seg() {} /* Seg */
    Seg(int H, int L, int R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN]; int sum[maxN], tab[maxN], cnt[maxN], val[10], n, m, T;

inline bool cmp(const Seg& a, const Seg& b) {return a.H < b.H;} /* cmp */

inline void update(int p, int l, int r)
{
    if (cnt[p]) sum[p] = tab[r] - tab[l];
    else if (r - l == 1) sum[p] = 0;
    else sum[p] = sum[lc] + sum[rc];
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && r <= R) {cnt[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("Farming.in" , "r", stdin );
    freopen("Farming.out", "w", stdout);
    for (scanf("%d", &T); T--;)
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < m; ++i) scanf("%d", val + i);
        int cnt_t = 0;
        for (int i = 0; i < n; ++i)
        {
            scanf("%d%d%d%d%d",
                    &node[i].x0, &node[i].y0,
                    &node[i].x1, &node[i].y1, &node[i].v);
            node[i].v = val[--node[i].v];
            tab[cnt_t++] = node[i].x0, tab[cnt_t++] = node[i].x1;
        } /* while */
        val[m++] = 0; std::sort(val, val + m);
        std::sort(tab, tab + cnt_t);
        cnt_t = std::unique(tab, tab + cnt_t) - tab;
        for (int i = 0; i < n; ++i)
            node[i].x0 = std::lower_bound(tab, tab + cnt_t, node[i].x0) - tab,
            node[i].x1 = std::lower_bound(tab, tab + cnt_t, node[i].x1) - tab;
        memset(cnt, 0, sizeof cnt);
        memset(sum, 0, sizeof sum);
        int64 ans = 0;
        for (int j = 0; j + 1 < m; ++j)
        {
            int cnt_s = 0;
            for (int i = 0; i < n; ++i) if (node[i].v > val[j]) //?? 为什么不能是>=?
                seg[cnt_s++] = Seg(node[i].y0, node[i].x0, node[i].x1,  1),
                seg[cnt_s++] = Seg(node[i].y1, node[i].x0, node[i].x1, -1);
            std::sort(seg, seg + cnt_s, cmp);
            int64 ths = 0;
            for (int i = 0; i < cnt_s; ++i)
            {
                if (seg[i].L < seg[i].R)
                    change(1, 0, cnt_t, seg[i].L, seg[i].R, seg[i].c);
                ths += (int64)sum[1] * (seg[i + 1].H - seg[i].H);
            } /* for */
            ans += ths * (val[j + 1] - val[j]);
        }
        static int Case = 0;
        printf("Case %d: %lld\n", ++Case, ans);
    } /* for */
    return 0;
} /* main */

/*

开始以为分别记录三个不同价值被加的次数就可以了,结果Wrong Answer。
看了题解,发现这道题仍然是一道长方体体积并的题目,将价值看成第三维坐标,然后直接算即可。

需要用long long类型。

*/


Uva11983 Weird Advertisement

/******************************************\
 * @prob: UVa11983 Weird Advertisement    *
 * @auth: Wang Junji  * @stat: Accepted.  *
 * @date: June. 28th, 2012                *
 * @memo: 矩形面积并、区间离散化             *
\******************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

typedef long long int64;
const int maxN = 30010;

struct Seg
{
    int H, L, R, c; Seg() {} /* Seg */
    Seg(int H, int L, int R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN << 1]; int tab[maxN << 1], sum[maxN << 3][12], cnt[maxN << 3], n, K, T;

inline bool cmp(const Seg& a, const Seg& b) {return a.H < b.H;} /* cmp */

inline void update(int p, int l, int r)
{
    for (int i = 0; i < std::min(cnt[p], K); ++i)
        sum[p][i] = tab[r] - tab[l];
    //注意这里要取cnt[p]和K的较小值,不然死得很惨……
    for (int i = cnt[p]; i < K; ++i)
        if (r - l == 1) sum[p][i] = 0;
        else sum[p][i] = sum[lc][i - cnt[p]] + sum[rc][i - cnt[p]];
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && r <= R) {cnt[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("Advertisement.in" , "r", stdin );
    freopen("Advertisement.out", "w", stdout);
    for (scanf("%d", &T); T--;)
    {
        scanf("%d%d", &n, &K);
        int cnt_t = 0, cnt_s = 0;
        while (n--)
        {
            int x0, y0, x1, y1;
            scanf("%d%d%d%d", &x0, &y0, &x1, &y1); ++x1, ++y1;
            tab[cnt_t++] = x0, tab[cnt_t++] = x1;
            seg[cnt_s++] = Seg(y0, x0, x1,  1);
            seg[cnt_s++] = Seg(y1, x0, x1, -1);
        } /* whlie */
        memset(sum, 0, sizeof sum);
        memset(cnt, 0, sizeof cnt);
        std::sort(tab, tab + cnt_t);
        cnt_t = std::unique(tab, tab + cnt_t) - tab;
        std::sort(seg, seg + cnt_s, cmp);
        int64 ans = 0;
        for (int i = 0; i < cnt_s; ++i)
        {
            int L = std::lower_bound(tab, tab + cnt_t, seg[i].L) - tab,
                R = std::lower_bound(tab, tab + cnt_t, seg[i].R) - tab;
            if (L < R) change(1, 0, cnt_t, L, R, seg[i].c);
            ans += (int64)sum[1][K - 1] * (seg[i + 1].H - seg[i].H);
        } /* for */
        static int Case = 0;
        printf("Case %d: %lld\n", ++Case, ans);
    } /* for */
    return 0;
} /* main */

/*

对矩形面积并稍微作了一下变形,由于K不是很大,所以可以直接保存覆盖1到K次的区间和。

*/


矩形周长

hdu1828 Pictiure

/******************************\
 * @prob: hdu1828 Picture     *
 * @auth: Wang Junji          *
 * @stat: Accepted.           *
 * @date: June. 27th, 2012    *
 * @memo: 矩形周长并、线段树     *
\******************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <string>

#define lc (p << 1)
#define rc (p << 1 | 1)
#define Mid ((l + r) >> 1)
#define LC lc, l, Mid
#define RC rc, Mid, r

const int maxN = 100010, INF = 0x3f3f3f3f;

struct Seg
{
    int H, L, R, c; Seg() {} /* Seg */
    Seg(int H, int L, int R, int c): H(H), L(L), R(R), c(c) {} /* Seg */
} seg[maxN];
bool covL[maxN], covR[maxN];
int num[maxN], len[maxN], cnt[maxN], n;

inline bool cmp(const Seg& a, const Seg& b)
{return a.H < b.H || (a.H == b.H && a.c > b.c);}
/* cmp */

inline int& gmin(int& a, const int& b) {return a < b ? a : (a = b);} /* gmin */
inline int& gmax(int& a, const int& b) {return a > b ? a : (a = b);} /* gmax */

inline void update(int p, int l, int r)
{
    if      (cnt[p]    ) covL[p] = covR[p] = true , num[p] = 2, len[p] = r - l;
    else if (r - l == 1) covL[p] = covR[p] = false, num[p] =    len[p] = 0;
    else
    {
        covL[p] = covL[lc], covR[p] = covR[rc];
        num[p] = num[lc] + num[rc];
        len[p] = len[lc] + len[rc];
        if (covR[lc] && covL[rc]) num[p] -= 2;
    } /* else */
    return;
} /* update */

void change(int p, int l, int r, int L, int R, int c)
{
    if (L <= l && r <= R) {cnt[p] += c; update(p, l, r); return;} /* if */
    if (L < Mid) change(LC, L, R, c);
    if (Mid < R) change(RC, L, R, c);
    update(p, l, r); return;
} /* change */

int main()
{
    freopen("picture.in" , "r", stdin );
    freopen("picture.out", "w", stdout);
    while (~scanf("%d", &n))
    {
        int min_L = INF, max_R = ~INF, cnt_s = 0;
        while (n--)
        {
            int L, R, lwr, upr;
            scanf("%d%d%d%d", &L, &lwr, &R, &upr);
            gmin(min_L, L), gmax(max_R, R);
            seg[cnt_s++] = Seg(lwr, L, R,  1);
            seg[cnt_s++] = Seg(upr, L, R, -1);
        } /* while */
        std::sort(seg, seg + cnt_s, cmp);
        memset(covL, 0, sizeof covL);
        memset(covR, 0, sizeof covR);
        memset(num , 0, sizeof num );
        memset(len , 0, sizeof len );
        memset(cnt , 0, sizeof cnt );
        int Last = 0, ans = 0;
        for (int i = 0; i < cnt_s; ++i, Last = len[1])
        {
            change(1, min_L, max_R, seg[i].L, seg[i].R, seg[i].c);
            ans += num[1] * (seg[i + 1].H - seg[i].H) + abs(len[1] - Last);
        } /* for */
        printf("%d\n", ans);
    } /* while */
    return 0;
} /* main */

/*

矩形的周长并,与面积并的不同,需要记录竖线段的个数num,并且用covL和covR分别记录一个区间的最左边和最右边是否被覆盖(目的是去掉重复线段),然后横竖线段分开统计。

*/

你可能感兴趣的:(【线段树】线段树及其相关 复习)