首页 > 其他 > 详细

【BZOJ3110】[ZJOI2013]K大数查询(整体二分)

时间:2018-11-24 18:35:45      阅读:159      评论:0      收藏:0      [点我收藏+]

题目:

BZOJ3110

分析:

整体二分模板题……

先明确一下题意:每个位置可以存放多个数,第一种操作是“加入 (insert) ”一个数而不是“加上 (add) ”一个数。

首先考虑只有一次询问的情况。设询问的名次为\(k\),我们二分出一个答案\(mid\),然后遍历所有修改。建立一棵区间线段树(下标是位置的线段树),对于一个给\([a,b]\)区间加入一个数\(c\)的修改,如果\(c\geq mid\),就给\([a,b]\)这个区间整体加\(1\)。最后查询询问区间的和,即这个区间中不小于\(mid\)的数的数量。如果这个数量大于等于\(k\)则向上二分,并记录答案,否则向下二分。这样单次询问的复杂度是\(O(mlog_2^2n)\)

可以看出,询问时最耗时间的是遍历所有修改并维护线段树。而这个操作只与当前二分到的\(mid\)有关,与询问无关。因此考虑把所有询问放在一个集合中,每次把询问分为将要“向上二分”(答案在\([mid+1,r]\))和“向下二分”(答案在\([l,mid]\)两个集合,并把可能对它们产生贡献的修改也分别加入这两个集合,然后分别递归下去。这就是“整体二分”。下面重点介绍如何对询问和修改分为两个集合。以下把修改和询问统称为“操作”。

询问的分法比较显然。如同只有一个询问的情况,把当前操作集合中的修改全部插入到线段树上。如果询问区间的和大于等于询问的名次则把这个询问分到“向上二分”的集合中,否则分到“向下二分”的集合中。

考虑修改。如果一个修改所加入的数不大于\(mid\),那么对于已经认定答案在\([mid+1,r]\)的询问一定是没有贡献的,所以只需要加到向下二分的集合中;如果一个修改所加入的数大于\(mid\),那么对于已经认定答案在\([l,mid]\)的询问一定是有\(1\)的贡献。如果把答案在\([l,mid]\)的询问所求的名次都减去\(1\),则这个修改也只会对向上二分的集合中的询问有贡献,只需要加到向上二分的集合中。这样每次都把所有操作分成独立的两部分,最多分\(log_2n\)次。每层所有操作集合的并集刚好是原集合,所以每层的时间复杂度是\(nlog_2n\),总复杂度\(O(nlog_2^2n)\)。具体流程可以参考代码。

代码:

并不需要真正把操作分成两个集合,只分它们的编号即可。

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstring>
using namespace std;

namespace zyt
{
    template<typename T>
    inline void read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != ‘-‘ && !isdigit(c));
        if (c == ‘-‘)
            f = true, c = getchar();
        do
            x = x * 10 + c - ‘0‘, c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
    }
    template<typename T>
    inline void write(T x)
    {
        static char buf[20];
        char *pos = buf;
        if (x < 0)
            putchar(‘-‘), x = -x;
        do
            *pos++ = x % 10 + ‘0‘;
        while (x /= 10);
        while (pos > buf)
            putchar(*--pos);
    }
    typedef long long ll;
    const int N = 5e4 + 10, B = 16, CHANGE = 1, QUERY = 2;
    int n, m, id[N], ans[N];
    struct node
    {
        int type, l, r;
        ll c;
    }opt[N];
    namespace Segment_Tree
    {
        struct node
        {
            ll sum, tag;
        }tree[1 << (B + 1)];
        inline void update(const int rot)
        {
            tree[rot].sum = tree[rot << 1].sum + tree[rot << 1 | 1].sum;
        }
        inline void push_down(const int rot, const int lt, const int rt)
        {
            if (tree[rot].tag)
            {
                ll &tag = tree[rot].tag;
                int mid = (lt + rt) >> 1;
                tree[rot << 1].sum += tag * (mid - lt + 1);
                tree[rot << 1].tag += tag;
                tree[rot << 1 | 1].sum += tag * (rt - mid);
                tree[rot << 1 | 1].tag += tag;
                tag = 0;
            }
        }
        void add(const int rot, const int lt, const int rt, const int ls, const int rs, const int x)
        {
            if (ls <= lt && rt <= rs)
            {
                tree[rot].sum += x * (rt - lt + 1), tree[rot].tag += x;
                return;
            }
            int mid = (lt + rt) >> 1;
            push_down(rot, lt, rt);
            if (ls <= mid)
                add(rot << 1, lt, mid, ls, rs, x);
            if (rs > mid)
                add(rot << 1 | 1, mid + 1, rt, ls, rs, x);
            update(rot);
        }
        ll query(const int rot, const int lt, const int rt, const int ls, const int rs)
        {
            if (ls <= lt && rt <= rs)
                return tree[rot].sum;
            int mid = (lt + rt) >> 1;
            ll ans = 0;
            push_down(rot, lt, rt);
            if (ls <= mid)
                ans += query(rot << 1, lt, mid, ls, rs);
            if (rs > mid)
                ans += query(rot << 1 | 1, mid + 1, rt, ls, rs);
            return ans;
        }
    }
    void solve(const int idl, const int idr, const int l, const int r)
    {//As for any question, determine
    //wheather there are more than opt[i].c numbers greater than mid or not
    //If so, ans[i] will be greater than mid. Otherwise less.
        using Segment_Tree::query;
        using Segment_Tree::add;
        static int newl[N], newr[N];
        if (l == r)
        {
            for (int i = idl; i <= idr; i++)
                if (opt[id[i]].type == QUERY)
                    ans[id[i]] = l;
            return;
        }
        int lcnt = 0, rcnt = 0;
        int mid = (l + r) >> 1;
        for (int i = idl; i <= idr; i++)
        {
            if (opt[id[i]].type == CHANGE)
            {
                if (opt[id[i]].c <= mid)
                    newl[lcnt++] = id[i];
                else
                {
                    add(1, 1, n, opt[id[i]].l, opt[id[i]].r, 1);
                    query(1, 1, n, 100, 100);
                    newr[rcnt++] = id[i];
                }
            }
            else
            {
                ll tmp = query(1, 1, n, opt[id[i]].l, opt[id[i]].r);
                if (tmp < opt[id[i]].c)
                    newl[lcnt++] = id[i], opt[id[i]].c -= tmp;
                else
                    newr[rcnt++] = id[i];
            }
        }
        for (int i = idl; i <= idr; i++)
            if (opt[id[i]].type == CHANGE && opt[id[i]].c > mid)
                add(1, 1, n, opt[id[i]].l, opt[id[i]].r, -1);
        memcpy(id + idl, newl, sizeof(int[lcnt]));
        memcpy(id + idl + lcnt, newr, sizeof(int[rcnt]));
        if (lcnt)
            solve(idl, idl + lcnt - 1, l, mid);
        if (rcnt)
            solve(idl + lcnt, idr, mid + 1, r);
    }
    int work()
    {
        read(n), read(m);
        for (int i = 1; i <= m; i++)
        {
            read(opt[i].type);
            read(opt[i].l);
            read(opt[i].r);
            read(opt[i].c);
            id[i] = i;
        }
        solve(1, m, -N, N);
        for (int i = 1; i <= m; i++)
            if (opt[i].type == QUERY)
                write(ans[i]), putchar(‘\n‘);
        return 0;
    }
}
int main()
{
    freopen("3110.in", "r", stdin);
    freopen("3110.out", "w", stdout);
    return zyt::work();
}

【BZOJ3110】[ZJOI2013]K大数查询(整体二分)

原文:https://www.cnblogs.com/zyt1253679098/p/10012800.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!