「CF1217F」Forced Online Queries Problem【线段树分治 或 ETT】

F. Forced Online Queries Problem

time limit per test 5 seconds
memory limit per test 256 megabytes
input standard input
output standard output

You are given an undirected graph with n n n vertices numbered from 1 1 1 to n n n. Initially there are no edges.

You are asked to perform some queries on the graph. Let l a s t last last be the answer to the latest query of the second type, it is set to 0 0 0 before the first such query. Then the queries are the following:

1   x   y ( 1 ≤ x , y ≤ n , x ! = y ) 1\ x\ y (1\leq x,y\leq n, x!=y) 1 x y(1x,yn,x!=y) — add an undirected edge between the vertices ( x + l a s t − 1 ) m o d   n + 1 (x+last−1) mod\ n+1 (x+last1)mod n+1 and ( y + l a s t − 1 ) m o d   n + 1 (y+last−1) mod\ n+1 (y+last1)mod n+1 if it doesn’t exist yet, otherwise remove it;
2   x   y ( 1 ≤ x , y ≤ n , x ! = y ) 2\ x\ y (1\leq x,y\leq n, x!=y) 2 x y(1x,yn,x!=y) — check if there exists a path between the vertices ( x + l a s t − 1 ) m o d   n + 1 (x+last−1) mod\ n+1 (x+last1)mod n+1 and ( y + l a s t − 1 ) m o d   n + 1 (y+last−1) mod\ n+1 (y+last1)mod n+1, which goes only through currently existing edges, and set l a s t last last to 1 1 1 if so and 0 0 0 otherwise.
Good luck!

Input

The first line contains two integer numbers n n n and m m m ( 2 ≤ n , m ≤ 2 × 1 0 5 ) (2\leq n,m\leq 2\times 10^5) (2n,m2×105) — the number of vertices and the number of queries, respectively.

Each of the following m m m lines contains a query of one of two aforementioned types. It is guaranteed that there is at least one query of the second type.

Output

Print a string, consisting of characters ′ 0 ′ '0' 0 and ′ 1 ′ '1' 1. The i − t h i-th ith character should be the answer to the i − t h i-th ith query of the second type. Therefore the length of the string should be equal to the number of queries of the second type.

Examples

input
5 9
1 1 2
1 1 3
2 3 2
1 2 4
2 3 4
1 2 4
2 3 4
1 1 3
2 4 3
output
1010
input
3 9
1 1 2
1 2 3
1 3 1
2 1 3
1 3 2
2 2 3
1 1 2
2 1 2
2 1 2
output
1101

Note

The converted queries in the first example are:


1 1 2
1 1 3
2 3 2
1 3 5
2 4 5
1 2 4
2 3 4
1 2 4
2 5 4

The converted queries in the second example are:


1 1 2
1 2 3
1 3 1
2 1 3
1 1 3
2 3 1
1 2 3
2 2 3
2 1 2

题意

  • 就是让你维护动态动态图的连通性,然后“强制在线”

题解

  • 首先离线做法的话就是一个线段树分治的裸题
  • 实际上是一个假的强制在线,因为 l a s t a n s lastans lastans只可能是0或者1,所以可以处理最多 2 × m 2\times m 2×m种询问,放到线段树上,然后在分治的过程中用一个 m a p map map记录一下每一条边是否有效,加到并查集的边的时候判断一下是否有效
  • 当然也可以在线做,做法就是 E T T ETT ETT,实际上这题是个原题,LOJ122,比赛的时候前几个大佬交的代码都是同一份 E T T ETT ETT板子

离线代码


#include<bits/stdc++.h>

using namespace std;
const int maxn=2e5+10;
const int maxm=4e5+10;

int n,m,ans[maxn],lastans=0;
map<int,int> exist[maxn];
struct operate{
    int opt,x,y;
    operate(int o=0,int a=0,int b=0) {
        opt=o;x=a;y=b;
    }
}o[maxm],sta[maxm];

namespace dsu{
    int fa[maxn],dep[maxn],tot;  //tot表示栈sta里的元素个数
    void init(int k) {
        tot=0;
        for(int i=1;i<=k;i++) fa[i]=i,dep[i]=0;
    }
    int find(int x) {
        return fa[x]==x?x:find(fa[x]);
    }
    void merge(int x,int y) {
        x=find(x),y=find(y);
        if(x==y) return;
        if(dep[x]>dep[y]) swap(x,y);
        if(dep[x]==dep[y]) dep[y]++;
        sta[++tot]=operate(0,x,y);  //注意栈中元素的构造函数
        fa[x]=y;
    }

    void undo(int id) {
        while(tot>id) {
            int x=sta[tot].x,y=sta[tot--].y;
            if(dep[y]==dep[x]+1) dep[y]--;
            fa[x]=x;
        }
    }
};
using namespace dsu;

namespace segment_tree{
    vector<operate> add[maxm<<2];
    void update(int id,int L,int R,int l,int r,operate k) {
        if(L>=l&&R<=r) {add[id].push_back(k);return;}
        int mid=(L+R)>>1;
        if(l<=mid) update(id<<1,L,mid,l,r,k);
        if(r>mid) update(id<<1|1,mid+1,R,l,r,k);
    }
    void work(int id,int L,int R) {
        int now=tot;
        for(int i=0;i<add[id].size();i++) {
            if(exist[add[id][i].x][add[id][i].y]) {
                merge(add[id][i].x,add[id][i].y);
            }
        }
        if(L==R) {
            if(o[L].opt==2) lastans=ans[L]=(find(o[L].x)==find(o[L].y));
            o[L+1].x=(o[L+1].x+lastans-1)%n+1,o[L+1].y=(o[L+1].y+lastans-1)%n+1;
            if(o[L+1].x>o[L+1].y) swap(o[L+1].x,o[L+1].y);
            if(o[L+1].opt==1) exist[o[L+1].x][o[L+1].y]^=1;
        }
        else {
            int mid=(L+R)>>1;
            work(id<<1,L,mid);work(id<<1|1,mid+1,R);
        }
        undo(now); //叶子结点也要undo
    }
};
using namespace segment_tree;


int main()
{
    scanf("%d %d",&n,&m);
    init(n);
    for(int i=1;i<=m;i++) {
        scanf("%d %d %d",&o[i].opt,&o[i].x,&o[i].y);
        if(o[i].x>o[i].y) swap(o[i].x,o[i].y);
        if(o[i].opt==1) {
            int x=o[i].x,y=o[i].y;
            if(exist[x].count(y)) update(1,1,m,exist[x][y],i,o[i]);
            exist[x][y]=i;

            x=x%n+1,y=y%n+1;
            if(x>y) swap(x,y);
            if(exist[x].count(y)) update(1,1,m,exist[x][y],i,operate(1,x,y));
            exist[x][y]=i;
        }
    }
    for(int i=1;i<=n;i++) for(map<int,int>::iterator it=exist[i].begin();it!=exist[i].end();it++) if(it->second!=m) update(1,1,m,it->second,m,operate(1,i,it->first));
    for(int i=1;i<=n;i++) exist[i].clear();
    if(o[1].opt==1) exist[o[1].x][o[1].y]=1;
    work(1,1,m);
    for(int i=1;i<=m;i++) if(o[i].opt==2) printf(ans[i]?"1":"0");
    return 0; 
}

在线做法( E T T ETT ETT)

#include "bits/stdc++.h"
using namespace std;

struct Xor128 {
    unsigned x, y, z, w;
    Xor128() : x(123456789), y(362436069), z(521288629), w(88675123) {}
    unsigned next() {
        unsigned t = x ^ (x << 11);
        x = y;
        y = z;
        z = w;
        return w = w ^ (w >> 19) ^ (t ^ (t >> 8));
    }
    inline unsigned next(unsigned n) { return next() % n; }
};

template <typename Node>
struct BottomupTreap {
    Xor128 rng;
    typedef Node *Ref;
    static int size(Ref t) { return !t ? 0 : t->size; }
    unsigned nextRand() { return rng.next(); }
private:
    bool choiceRandomly(Ref l, Ref r) { return l->priority < r->priority; }
public:
    Ref join(Ref l, Ref r) {
        if (!l) return r;
        if (!r) return l;
        Ref t = NULL;
        unsigned long long dirs = 0;
        int h;
        for(h = 0;; ++h) {
            if (h >= sizeof(dirs) * 8 - 2) { t = join(l->right, r->left);dirs = dirs << 2 | 1; h++; break;}
            dirs <<= 1;
            if (choiceRandomly(l, r)) {
                Ref c = l->right;
                if (!c) {t = r; r = r->parent; break; }
                l = c;
            } else {
                dirs |= 1;
                Ref c = r->left;
                if (!c) { t = l; l = l->parent; break; }
                r = c;
            }
        }
        for (; h >= 0; --h) {
            if (!(dirs & 1)) { Ref p = l->parent; t = l->linkr(t); l = p; } 
            else {Ref p = r->parent; t = r->linkl(t); r = p; }
            dirs >>= 1;
        }
        return t;
    }
    typedef pair<Ref, Ref> RefPair;
    RefPair split2(Ref t) {
        Ref p, l = t->left, r = t;
        Node::cut(l);
        t->linkl(NULL);
        while ((p = t->parent)) {
            t->parent = NULL;
            if (p->left == t) r = p->linkl(r);
            else l = p->linkr(l);
            t = p;
        }
        return RefPair(l, r);
    }
    RefPair split3(Ref t) {
        Ref p, l = t->left, r = t->right;
        Node::cut(l), Node::cut(r);
        t->linklr(NULL, NULL);
        while ((p = t->parent)) {
            t->parent = NULL;
            if (p->left == t) r = p->linkl(r);
            else l = p->linkr(l);
            t = p;
        }
        return RefPair(l, r);
    }
    Ref cons(Ref h, Ref t) {
        assert(size(h) == 1);
        if (!t) return h;
        Ref u = NULL;
        while (true) {
            if (choiceRandomly(h, t)) { Ref p = t->parent; u = h->linkr(t); t = p; break; }
            Ref l = t->left;
            if (!l) { u = h; break; }
            t = l;
        }
        while (t) {
            u = t->linkl(u);
            t = t->parent;
        }
        return u;
    }
};

class EulerTourTreeWithMarks {
    struct Node {
        typedef BottomupTreap<Node> BST;
        Node *left, *right, *parent;
        int size;
        unsigned priority;
        char marks, markUnions;
        Node() : left(NULL), right(NULL), parent(NULL), size(1), priority(0), marks(0), markUnions(0) {}
        inline Node *update() {
            int size_t = 1, markUnions_t = marks;
            if (left) {
                size_t += left->size;
                markUnions_t |= left->markUnions;
            }
            if (right) {
                size_t += right->size;
                markUnions_t |= right->markUnions;
            }
            size = size_t, markUnions = markUnions_t;
            return this;
        }
        inline Node *linkl(Node *c) {
            if ((left = c)) c->parent = this;
            return update();
        }
        inline Node *linkr(Node *c) {
            if ((right = c)) c->parent = this;
            return update();
        }
        inline Node *linklr(Node *l, Node *r) {
            if ((left = l)) l->parent = this;
            if ((right = r)) r->parent = this;
            return update();
        }
        static Node *cut(Node *t) {
            if (t) t->parent = NULL;
            return t;
        }

        static const Node *findRoot(const Node *t) {
            while (t->parent) t = t->parent;
            return t;
        }
        static pair<Node *, int> getPosition(Node *t) {
            int k = BST::size(t->left);
            Node *p;
            while ((p = t->parent)) {
                if (p->right == t) k += BST::size(p->left) + 1;
                t = p;
            }
            return make_pair(t, k);
        }
        static const Node *findHead(const Node *t) {
            while (t->left) t = t->left;
            return t;
        }
        static void updatePath(Node *t) {
            while (t) {
                t->update();
                t = t->parent;
            }
        }
    };
    typedef Node::BST BST;
    BST bst;
    vector<Node> nodes;
    vector<int> firstArc;
    vector<bool> edgeMark, vertexMark;
    inline int getArcIndex(const Node *a) const { return a - &nodes[0]; }
    inline int arc1(int ei) const { return ei; }
    inline int arc2(int ei) const { return ei + (numVertices() - 1); }
public:
    inline int numVertices() const { return firstArc.size(); }
    inline int numEdges() const { return numVertices() - 1; }
    inline bool getEdgeMark(int a) const { return a < numEdges() ? edgeMark[a] : false; }
    inline bool getVertexMark(int v) const { return vertexMark[v]; }

private:
    void updateMarks(int a, int v) {
        Node *t = &nodes[a];
        t->marks = getEdgeMark(a) << 0 | getVertexMark(v) << 1;
        Node::updatePath(t);
    }
    void firstArcChanged(int v, int a, int b) {
        if (a != -1) updateMarks(a, v);
        if (b != -1) updateMarks(b, v);
    }

public:
    class TreeRef {
        friend class EulerTourTreeWithMarks;
        const Node *ref;

    public:
        TreeRef() {}
        TreeRef(const Node *ref_) : ref(ref_) {}
        bool operator==(const TreeRef &that) const { return ref == that.ref; }
        bool operator!=(const TreeRef &that) const { return ref != that.ref; }
        bool isIsolatedVertex() const { return ref == NULL; }
    };

    void init(int N) {
        int M = N - 1;
        firstArc.assign(N, -1);
        nodes.assign(M * 2, Node());
        for (int i = 0; i < M * 2; i++) nodes[i].priority = bst.nextRand();
        edgeMark.assign(M, false);
        vertexMark.assign(N, false);
    }

    TreeRef getTreeRef(int v) const {
        int a = firstArc[v];
        return TreeRef(a == -1 ? NULL : Node::findRoot(&nodes[a]));
    }

    bool isConnected(int v, int w) const {
        if (v == w) return true;
        int a = firstArc[v], b = firstArc[w];
        if (a == -1 || b == -1) return false;
        return Node::findRoot(&nodes[a]) == Node::findRoot(&nodes[b]);
    }

    static int getSize(TreeRef t) {
        if (t.isIsolatedVertex()) return 1;
        else return t.ref->size / 2 + 1;
    }

    void link(int ti, int v, int w) {
        int a1 = arc1(ti), a2 = arc2(ti);
        if (v > w) swap(a1, a2);
        int va = firstArc[v], wa = firstArc[w];
        Node *l, *m, *r;
        if (va != -1) {
            pair<Node *, Node *> p = bst.split2(&nodes[va]);
            m = bst.join(p.second, p.first);
        } else {
            m = NULL;
            firstArc[v] = a1;
            firstArcChanged(v, -1, a1);
        }
        if (wa != -1) {
            pair<Node *, Node *> p = bst.split2(&nodes[wa]);
            l = p.first, r = p.second;
        } else {
            l = r = NULL;
            firstArc[w] = a2;
            firstArcChanged(w, -1, a2);
        }
        m = bst.cons(&nodes[a2], m);
        r = bst.cons(&nodes[a1], r);
        bst.join(bst.join(l, m), r);
    }

    void cut(int ti, int v, int w) {
        if (v > w)swap(v, w);
        int a1 = arc1(ti), a2 = arc2(ti);
        pair<Node *, Node *> p = bst.split3(&nodes[a1]);
        int prsize = BST::size(p.second);
        pair<Node *, Node *> q = bst.split3(&nodes[a2]);
        Node *l, *m, *r;
        if (p.second == &nodes[a2] || BST::size(p.second) != prsize) {
            l = p.first, m = q.first, r = q.second;
        }else {
            swap(v, w);swap(a1, a2);
            l = q.first, m = q.second, r = p.second;
        }
        if (firstArc[v] == a1) {
            int b;
            if (r != NULL) b = getArcIndex(Node::findHead(r));
            else b = !l ? -1 : getArcIndex(Node::findHead(l));
            firstArc[v] = b;
            firstArcChanged(v, a1, b);
        }
        if (firstArc[w] == a2) {
            int b = !m ? -1 : getArcIndex(Node::findHead(m));
            firstArc[w] = b;
            firstArcChanged(w, a2, b);
        }

        bst.join(l, r);
    }

    void changeEdgeMark(int ti, bool b) {
        assert(ti < numEdges());
        edgeMark[ti] = b;
        Node *t = &nodes[ti];
        t->marks = (b << 0) | (t->marks & (1 << 1));
        Node::updatePath(t);
    }
    void changeVertexMark(int v, bool b) {
        vertexMark[v] = b;
        int a = firstArc[v];
        if (a != -1) {
            Node *t = &nodes[a];
            t->marks = (t->marks & (1 << 0)) | (b << 1);
            Node::updatePath(t);
        }
    }

    template <typename Callback>
    bool enumMarkedEdges(TreeRef tree, Callback callback) const {
        return enumMarks<0, Callback>(tree, callback);
    }
    template <typename Callback>
    bool enumMarkedVertices(TreeRef tree, Callback callback) const {
        return enumMarks<1, Callback>(tree, callback);
    }

private:
    template <int Mark, typename Callback>
    bool enumMarks(TreeRef tree, Callback callback) const {
        if (tree.isIsolatedVertex())
            return true;
        const Node *t = tree.ref;
        if (t->markUnions >> Mark & 1)
            return enumMarksRec<Mark, Callback>(t, callback);
        else
            return true;
    }
    template <int Mark, typename Callback>
    bool enumMarksRec(const Node *t, Callback callback) const {
        const Node *l = t->left, *r = t->right;
        if (l && (l->markUnions >> Mark & 1)) if (!enumMarksRec<Mark, Callback>(l, callback)) return false;
        if (t->marks >> Mark & 1) if (!callback(getArcIndex(t))) return false;
        if (r && (r->markUnions >> Mark & 1)) if (!enumMarksRec<Mark, Callback>(r, callback)) return false;
        return true;
    }

public:
    void debugEnumEdges(vector<int> &out_v) const {
        int M = numEdges();
        for (int ti = 0; ti < M; ti++) {
            const Node *t = &nodes[ti];
            if (t->left || t->right || t->parent)
                out_v.push_back(ti);
        }
    }
};

class HolmDeLichtenbergThorup {
    typedef HolmDeLichtenbergThorup This;
    typedef EulerTourTreeWithMarks Forest;
    typedef Forest::TreeRef TreeRef;
    int numVertices_m;
    int numSamplings;
    vector<Forest> forests;
    vector<char> edgeLevel;
    vector<int> treeEdgeIndex;         
    vector<int> treeEdgeMap;          
    vector<int> treeEdgeIndexFreeList;  
    vector<int> arcHead;
    vector<vector<int>> firstIncidentArc;
    vector<int> nextIncidentArc, prevIncidentArc;
    vector<bool> edgeVisited;
    vector<int> visitedEdges; 
    int arc1(int ei) const { return ei; }
    int arc2(int ei) const { return numMaxEdges() + ei; }
    int arcEdge(int i) const { return i >= numMaxEdges() ? i - numMaxEdges() : i; }
    bool replace(int lv, int v, int w) {
        Forest &forest = forests[lv];
        TreeRef vRoot = forest.getTreeRef(v), wRoot = forest.getTreeRef(w);
        assert(vRoot.isIsolatedVertex() || wRoot.isIsolatedVertex() || vRoot != wRoot);
        int vSize = forest.getSize(vRoot), wSize = forest.getSize(wRoot);
        int u,uSize;
        TreeRef uRoot;
        if (vSize <= wSize) u = v, uRoot = vRoot, uSize = vSize;
        else u = w, uRoot = wRoot, uSize = wSize;
        int replacementEdge = -1;
        enumIncidentArcs(forest, uRoot, u, lv, FindReplacementEdge(uRoot, &replacementEdge));
        if (replacementEdge != -1 && (int)visitedEdges.size() + 1 <= numSamplings) {
            deleteNontreeEdge(replacementEdge);
            addTreeEdge(replacementEdge);
            for (int i = 0; i < (int)visitedEdges.size(); i++) edgeVisited[visitedEdges[i]] = false;
            visitedEdges.clear();
            return true;
        }
        for (int i = 0; i < (int)visitedEdges.size(); i++) {
            int ei = visitedEdges[i];
            edgeVisited[ei] = false;
            deleteNontreeEdge(ei);
            ++edgeLevel[ei];
            insertNontreeEdge(ei);
        }
        visitedEdges.clear();
        forest.enumMarkedEdges(uRoot, EnumLevelTreeEdges(this));
        for (int i = 0; i < (int)visitedEdges.size(); i++) {
            int ti = visitedEdges[i];
            int ei = treeEdgeMap[ti];
            int v = arcHead[arc2(ei)], w = arcHead[arc1(ei)];
            int lv = edgeLevel[ei];
            edgeLevel[ei] = lv + 1;
            forests[lv].changeEdgeMark(ti, false);
            forests[lv + 1].changeEdgeMark(ti, true);
            forests[lv + 1].link(ti, v, w);
        }
        visitedEdges.clear();
        if (replacementEdge != -1) {
            deleteNontreeEdge(replacementEdge);
            addTreeEdge(replacementEdge);
            return true;
        }else if (lv > 0) return replace(lv - 1, v, w);
        else return false;
    }

    struct EnumLevelTreeEdges {
        This *thisp;
        EnumLevelTreeEdges(This *thisp_) : thisp(thisp_) {}

        inline bool operator()(int a) {
            thisp->enumLevelTreeEdges(a);
            return true;
        }
    };
    void enumLevelTreeEdges(int ti) { visitedEdges.push_back(ti); }

    template <typename Callback>
    bool enumIncidentArcs(Forest &forest, TreeRef t, int u, int lv, Callback callback) {
        if (t.isIsolatedVertex())
            return enumIncidentArcsWithVertex<Callback>(lv, u, callback);
        else
            return forest.enumMarkedVertices(t, EnumIncidentArcs<Callback>(this, lv, callback));
    }

    template <typename Callback>
    struct EnumIncidentArcs {
        This *thisp;
        int lv;
        Callback callback;
        EnumIncidentArcs(This *thisp_, int lv_, Callback callback_)
            : thisp(thisp_), lv(lv_), callback(callback_) {}
        inline bool operator()(int tii) const {
            return thisp->enumIncidentArcsWithTreeArc(tii, lv, callback);
        }
    };

    template <typename Callback>
    bool enumIncidentArcsWithTreeArc(int tii, int lv, Callback callback) {
        bool dir = tii >= numVertices() - 1;
        int ti = dir ? tii - (numVertices() - 1) : tii;
        int ei = treeEdgeMap[ti];
        int v = arcHead[arc2(ei)], w = arcHead[arc1(ei)];
        int u = !(dir != (v > w)) ? v : w;
        return enumIncidentArcsWithVertex(lv, u, callback);
    }
    template <typename Callback>
    bool enumIncidentArcsWithVertex(int lv, int u, Callback callback) {
        int it = firstIncidentArc[lv][u];
        while (it != -1) {
            if (!callback(this, it))
                return false;
            it = nextIncidentArc[it];
        }
        return true;
    }

    struct FindReplacementEdge {
        TreeRef uRoot;
        int *replacementEdge;
        FindReplacementEdge(TreeRef uRoot_, int *replacementEdge_)
            : uRoot(uRoot_), replacementEdge(replacementEdge_) {}
        inline bool operator()(This *thisp, int a) const {
            return thisp->findReplacementEdge(a, uRoot, replacementEdge);
        }
    };
    bool findReplacementEdge(int a, TreeRef uRoot, int *replacementEdge) {
        int ei = arcEdge(a);
        if (edgeVisited[ei]) return true;
        int lv = edgeLevel[ei];
        TreeRef hRoot = forests[lv].getTreeRef(arcHead[a]);
        if (hRoot.isIsolatedVertex() || hRoot != uRoot) {
            *replacementEdge = ei;
            return false;
        }
        edgeVisited[ei] = true;
        visitedEdges.push_back(ei);
        return true;
    }

    void addTreeEdge(int ei) {
        int v = arcHead[arc2(ei)], w = arcHead[arc1(ei)];
        int lv = edgeLevel[ei];
        int ti = treeEdgeIndexFreeList.back();
        treeEdgeIndexFreeList.pop_back();
        treeEdgeIndex[ei] = ti;
        treeEdgeMap[ti] = ei;
        forests[lv].changeEdgeMark(ti, true);
        for (int i = 0; i <= lv; i++) forests[i].link(ti, v, w);
    }

    void insertIncidentArc(int a, int v) {
        int ei = arcEdge(a);
        int lv = edgeLevel[ei];
        assert(treeEdgeIndex[ei] == -1);
        int next = firstIncidentArc[lv][v];
        firstIncidentArc[lv][v] = a;
        nextIncidentArc[a] = next;
        prevIncidentArc[a] = -1;
        if (next != -1) prevIncidentArc[next] = a;
        if (next == -1) forests[lv].changeVertexMark(v, true);
    }

    void deleteIncidentArc(int a, int v) {
        int ei = arcEdge(a);
        int lv = edgeLevel[ei];
        assert(treeEdgeIndex[ei] == -1);
        int next = nextIncidentArc[a], prev = prevIncidentArc[a];
        nextIncidentArc[a] = prevIncidentArc[a] = -2;
        if (next != -1) prevIncidentArc[next] = prev;
        if (prev != -1) nextIncidentArc[prev] = next;
        else firstIncidentArc[lv][v] = next;
        if (next == -1 && prev == -1) forests[lv].changeVertexMark(v, false);
    }
    void insertNontreeEdge(int ei) {
        int a1 = arc1(ei), a2 = arc2(ei);
        insertIncidentArc(a1, arcHead[a2]);
        insertIncidentArc(a2, arcHead[a1]);
    }
    void deleteNontreeEdge(int ei) {
        int a1 = arc1(ei), a2 = arc2(ei);
        deleteIncidentArc(a1, arcHead[a2]);
        deleteIncidentArc(a2, arcHead[a1]);
    }
public:
    HolmDeLichtenbergThorup() : numVertices_m(0), numSamplings(0) {}
    int numVertices() const { return numVertices_m; }
    int numMaxEdges() const { return edgeLevel.size(); }
    void init(int N, int M) {
        numVertices_m = N;
        int levels = 1;
        while (1 << levels <= N / 2) levels++;
        numSamplings = (int)(levels * 1);
        forests.resize(levels);
        for (int lv = 0; lv < levels; lv++) forests[lv].init(N);
        edgeLevel.assign(M, -1);
        treeEdgeIndex.assign(M, -1);
        treeEdgeMap.assign(N - 1, -1);
        treeEdgeIndexFreeList.resize(N - 1);
        for (int ti = 0; ti < N - 1; ti++) treeEdgeIndexFreeList[ti] = ti;
        arcHead.assign(M * 2, -1);
        firstIncidentArc.resize(levels);
        for (int lv = 0; lv < levels; lv++) firstIncidentArc[lv].assign(N, -1);
        nextIncidentArc.assign(M * 2, -2);
        prevIncidentArc.assign(M * 2, -2);
        edgeVisited.assign(M, false);
    }

    bool insertEdge(int ei, int v, int w) {
        if (!(0 <= ei && ei < numMaxEdges() && 0 <= v && v < numVertices() && 0 <= w && w < numVertices())) {
            system("pause");
        }
        assert(0 <= ei && ei < numMaxEdges() && 0 <= v && v < numVertices() && 0 <= w && w < numVertices());
        assert(edgeLevel[ei] == -1);
        int a1 = arc1(ei), a2 = arc2(ei);
        arcHead[a1] = w, arcHead[a2] = v;
        bool treeEdge = !forests[0].isConnected(v, w);
        edgeLevel[ei] = 0;
        if (treeEdge) addTreeEdge(ei);
        else {
            treeEdgeIndex[ei] = -1;
            if (v != w) insertNontreeEdge(ei);
        }
        return treeEdge;
    }

    bool deleteEdge(int ei) {
        assert(0 <= ei && ei < numMaxEdges() && edgeLevel[ei] != -1);
        int a1 = arc1(ei), a2 = arc2(ei);
        int v = arcHead[a2], w = arcHead[a1];
        int lv = edgeLevel[ei];
        int ti = treeEdgeIndex[ei];
        bool splitted = false;
        if (ti != -1) {
            treeEdgeMap[ti] = -1;
            treeEdgeIndex[ei] = -1;
            treeEdgeIndexFreeList.push_back(ti);
            for (int i = 0; i <= lv; i++) forests[i].cut(ti, v, w);
            forests[lv].changeEdgeMark(ti, false);
            splitted = !replace(lv, v, w);
        }else if(v != w) deleteNontreeEdge(ei);
        arcHead[a1] = arcHead[a2] = -1;
        edgeLevel[ei] = -1;
        return splitted;
    }

    bool isConnected(int v, int w) const { return forests[0].isConnected(v, w); }
};
vector<int> te;
map<int, map<int, int>> ee;
map<pair<int,int>,int> memor;
int lastans = 0,n,m,opt,u,v;
int main() {
    scanf("%d %d",&n,&m);
    for (int i = m; i > 0; --i) te.push_back(i);
    typedef HolmDeLichtenbergThorup FullyDynamicConnectivity;
    FullyDynamicConnectivity fdc;
    fdc.init(n + 1, m + 1);
    while(m--){
        scanf("%d %d %d",&opt,&u,&v);
        u=(u+lastans-1)%n+1;
        v=(v+lastans-1)%n+1;
        if(u>v) swap(u,v);
        if(opt==1) {
            if(memor.count(make_pair(u,v))) {
                fdc.deleteEdge(ee[u][v]);
                ee[u].erase(v);
                memor.erase(make_pair(u,v));
            }else {
                ee[u][v] = te.back();
                te.pop_back();
                fdc.insertEdge(ee[u][v], u, v);
                memor[make_pair(u,v)]=1;
            }
        }else {
            lastans=fdc.isConnected(u,v);
            printf("%c",'0'+lastans);
        }
    }
    return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值