【BZOJ3514】Codechef MARCH14 GERALD07加强版(LCT_主席树)

题目:

BZOJ3514

分析:

看到这题真的是一脸懵逼无从下手,只好膜题解。看到「森林的联通块数 = 点数 - 边数」这一句话就立刻什么都会了 QAQ 。

这题最重要的就是意识到上面那个式子(正确性显然)。那么这个问题就变成了:\([l,r]\) 中最多选出多少条边,使得图中不存在环。根据 Kruskal 的原理,贪心地选就能保证选出的边最多,所以我们不妨假定尽量选编号较大的边。

给每条边 \(i\)\(nxt_i\) ,表示从 \(i\) 开始向后依次插入边,插入到 \(nxt_i\) 这条边时会出现 包含 \(i\) 环(不存在则为无穷大)。即,如果 \(i\)\(nxt_i\) 同时可选,由于尽量选择标号大的边,所以选上 \(nxt_i\) 而把 \(i\) 删掉。\(nxt_i\) 也可理解为会「废掉」\(i\) 的第一条边。那么答案就是满足 \(i\in[l,r]\)\(nxt[i]\notin[l,r]\) 的边数。这是一个二维数点问题,直接用主席树解决即可。

如何 \(O(n)\) 求出 \(nxt_i\) 呢?考虑从小到大加边,当加入边 \(i\) 时,如果出现了环,那么断掉环上最小的边 \(j\) ,则 \(nxt_j=i\) 。这个过程用 LCT 维护。

代码:

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

namespace zyt
{
    template<typename T>
    inline bool read(T &x)
    {
        char c;
        bool f = false;
        x = 0;
        do
            c = getchar();
        while (c != EOF && c != '-' && !isdigit(c));
        if (c == EOF)
            return false;
        if (c == '-')
            f = true, c = getchar();
        do
            x = x * 10 + c - '0', c = getchar();
        while (isdigit(c));
        if (f)
            x = -x;
        return true;
    }
    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);
    }
    const int N = 2e5 + 10, M = 2e5 + 10, B = 20, P = N + M, INF = 0x3f3f3f3f;
    class Link_Cut_Tree
    {
    private:
        struct nulltag {};
        struct node
        {
            int val, min;
            bool revtag;
            node *fa, *s[2];
            node(nulltag)
                : val(INF), min(INF), revtag(false)
            {
                fa = s[0] = s[1] = this;
            }
            node(const int _val)
                : val(_val), min(_val), revtag(false)
            {
                fa = s[0] = s[1] = null;
            }
        }*pos[P];
        static node *null;
        static void update(node *const rot)
        {
            rot->min = min(rot->val, min(rot->s[0]->min, rot->s[1]->min));
        }
        static void rev(node *const rot)
        {
            swap(rot->s[0], rot->s[1]), rot->revtag ^= 1;
        }
        static void pushdown(node *const rot)
        {
            if (rot->revtag)
            {
                if (rot->s[0] != null)
                    rev(rot->s[0]);
                if (rot->s[1] != null)
                    rev(rot->s[1]);
                rot->revtag = 0;
            }
        }
        static void pushdown_all(node *const rot)
        {
            if (!isrot(rot))
                pushdown_all(rot->fa);
            pushdown(rot);
        }
        static bool isrot(const node *const rot)
        {
            return rot != rot->fa->s[0] && rot != rot->fa->s[1];
        }
        static bool dir(const node *const rot)
        {
            return rot == rot->fa->s[1];
        }
        static void rotate(node *const rot)
        {
            node *f = rot->fa, *ff = f->fa;
            bool d = dir(rot);
            if (!isrot(f))
                ff->s[dir(f)] = rot;
            rot->fa = ff;
            f->s[d] = rot->s[!d];
            if (rot->s[!d] != null)
                rot->s[!d]->fa = f;
            rot->s[!d] = f;
            f->fa = rot;
            update(f);
        }
        static void splay(node *const rot)
        {
            pushdown_all(rot);
            while (!isrot(rot))
            {
                node *f = rot->fa;
                if (isrot(f))
                    rotate(rot);
                else if (dir(f) ^ dir(rot))
                    rotate(rot), rotate(rot);
                else
                    rotate(f), rotate(rot);
            }
            update(rot);
        }
        static void access(node *rot)
        {
            for (node *last = null; rot != null; last = rot, rot = rot->fa)
                splay(rot), rot->s[1] = last, update(rot);
        }
        static void mkrot(node *const rot)
        {
            access(rot), splay(rot), rev(rot);
        }
        static node *findrot(node *rot)
        {
            access(rot), splay(rot);
            while (rot->s[0] != null)
                rot = rot->s[0];
            return rot;
        }
    public:
        bool connect(const int x, const int y)
        {
            return findrot(pos[x]) == findrot(pos[y]);
        }
        void link(const int x, const int y)
        {
            node *rot1 = pos[x], *rot2 = pos[y];
            mkrot(rot1), splay(rot1), rot1->fa = rot2;
        }
        void cut(const int x, const int y)
        {
            node *rot1 = pos[x], *rot2 = pos[y];
            mkrot(rot1), access(rot2), splay(rot1);
            rot1->s[1] = rot2->fa = null;
            update(rot1);
        }
        int query(const int x, const int y)
        {
            node *rot1 = pos[x], *rot2 = pos[y];
            mkrot(rot1), access(rot2), splay(rot1);
            return rot1->min;
        }
        void init(const int n, const int *const w)
        {
            for (int i = 1; i <= n; i++)
                pos[i] = new node(w[i]);
        }

    }lct;
    typedef Link_Cut_Tree LCT;
    LCT::node *LCT::null = new LCT::node(LCT::nulltag());
    namespace Chairman_Tree
    {
        struct node
        {
            int sum, lt, rt;
        }tree[M * B];
        int cnt;
        void add(int &rot, const int pre, const int lt, const int rt, const int pos, const int x)
        {
            rot = ++cnt;
            tree[rot] = tree[pre];
            tree[rot].sum += x;
            if (lt == rt)
                return;
            int mid = (lt + rt) >> 1;
            if (pos <= mid)
                add(tree[rot].lt, tree[pre].lt, lt, mid, pos, x);
            else
                add(tree[rot].rt, tree[pre].rt, mid + 1, rt, pos, x);
        }
        int query(const int rot1, const int rot2, const int lt, const int rt, const int ls, const int rs)
        {
            if (!rot2 || (ls <= lt && rt <= rs))
                return tree[rot2].sum - tree[rot1].sum;
            int mid = (lt + rt) >> 1;
            if (rs <= mid)
                return query(tree[rot1].lt, tree[rot2].lt, lt, mid, ls, rs);
            else if (ls > mid)
                return query(tree[rot1].rt, tree[rot2].rt, mid + 1, rt, ls, rs);
            else
                return query(tree[rot1].lt, tree[rot2].lt, lt, mid, ls, rs)
                    + query(tree[rot1].rt, tree[rot2].rt, mid + 1, rt, ls, rs);
        }
    }
    typedef pair<int, int> pii;
    int head[M], w[P], n, m, nxt[M];
    pii arr[M];
    int work()
    {
        using namespace Chairman_Tree;
        int lastans = 0, q, type;
        read(n), read(m), read(q), read(type);
        for (int i = 1; i <= n; i++)
            w[i] = INF;
        for (int i = 1; i <= m; i++)
            w[i + n] = i, nxt[i] = m + 1;
        lct.init(n + m, w);
        for (int i = 1; i <= m; i++)
        {
            int a, b;
            read(a), read(b);
            arr[i] = pii(a, b);
            if (a == b)
            {
                nxt[i] = i;
                continue;
            }
            if (lct.connect(a, b))
            {
                int tmp = lct.query(a, b);
                nxt[tmp] = i;
                lct.cut(tmp + n, arr[tmp].first);
                lct.cut(tmp + n, arr[tmp].second);
            }
            lct.link(a, i + n), lct.link(i + n, b);
        }
        for (int i = 1; i <= m; i++)
            add(head[i], head[i - 1], 1, m + 1, nxt[i], 1);
        while (q--)
        {
            int l, r;
            read(l), read(r);
            if (type)
                l ^= lastans, r ^= lastans;
            write(lastans = (n - query(head[l - 1], head[r], 1, m + 1, r + 1, m + 1)));
            putchar('\n');
        }
        return 0;
    }
}
int main()
{
    return zyt::work();
}

猜你喜欢

转载自www.cnblogs.com/zyt1253679098/p/10881859.html