Submission #164534


Source Code Expand

#define _CRT_SECURE_NO_WARNINGS
#include <string>
#include <vector>
#include <algorithm>
#include <numeric>
#include <set>
#include <map>
#include <queue>
#include <iostream>
#include <sstream>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstring>
#include <cctype>
#include <cassert>
#include <complex>
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define rer(i,l,u) for(int (i)=(int)(l);(i)<=(int)(u);++(i))
#define reu(i,l,u) for(int (i)=(int)(l);(i)<(int)(u);++(i))
#if defined(_MSC_VER) || __cplusplus > 199711L
#define aut(r,v) auto r = (v)
#else
#define aut(r,v) typeof(v) r = (v)
#endif
#define each(it,o) for(aut(it, (o).begin()); it != (o).end(); ++ it)
#define all(o) (o).begin(), (o).end()
#define pb(x) push_back(x)
#define mp(x,y) make_pair((x),(y))
#define mset(m,v) memset(m,v,sizeof(m))
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef vector<int> vi; typedef pair<int,int> pii; typedef vector<pair<int,int> > vpii;
typedef long long ll; typedef vector<long long> vl; typedef pair<long long,long long> pll; typedef vector<pair<long long,long long> > vpll;
typedef vector<string> vs; typedef long double ld;
template<typename T, typename U> inline void amin(T &x, U y) { if(y < x) x = y; }
template<typename T, typename U> inline void amax(T &x, U y) { if(x < y) x = y; }

//・わりと遅い…まあでもlog^2 nといわれればそんな感じではある
//・サンプリングとTest-Allっぽいものを導入してみたけどうまくいってるのかわからない…
//
//References
//・Holm, Jacob, Kristian De Lichtenberg, and Mikkel Thorup. "Poly-logarithmic deterministic fully-dynamic algorithms for connectivity, minimum spanning tree, 2-edge, and biconnectivity." Journal of the ACM (JACM) 48.4 (2001): 723-760.
//・Thorup, Mikkel. "Near-optimal fully-dynamic graph connectivity." Proceedings of the thirty-second annual ACM symposium on Theory of computing. ACM, 2000.
class FullyDynamicConnectivity {
	//local search treeとglobalな構造は同じノードで表して、ternary search treeっぽくやる
	struct Node {
		Node *left, *right;	//local treeの子ノード
		Node *mid;			//所有するlocal treeの根
		Node *parent;		//local/global treeの親ノード
		int numLeafs;		//global tree上の子木の葉の数(たとえlocal tree上で親子関係があったとしても兄弟ノードの数は足されないことに注意)
		int treeEdges, nonTreeEdges;	//ビットセットで、ビットiはlocal treeとglobal tree上の子孫の頂点にレベルiの木(resp.非木)辺が1つ以上接しているかを表す
		int level;			//global tree上のレベル
		Node():
			left(NULL), right(NULL), mid(NULL), parent(NULL),
			numLeafs(0), treeEdges(0), nonTreeEdges(0), level(-1) { }
	};
	std::vector<Node> vertexNodes;
	std::vector<bool> isTreeEdge;
	//その辺が存在しないことを-1で表す
	std::vector<int> edgeLevel;
	//無向辺: edge, 有向辺: arc と呼び分けることにする
	//arcの先の頂点
	std::vector<int> arcHead;		//Edge×2 → Vertex
	//頂点ごとにレベルごとにtree/non tree edgesが存在するかどうかのbitset
	std::vector<int> levelTreeEdges, levelNonTreeEdges;	//Vertex -> Bitset
	//頂点ごとのincident arcsの連結リスト
	std::vector<int> firstTreeArc, firstNonTreeArc;	//Vertex → Edge×2+⊥
	//特定のレベルのリストをO(log n)で取得できるようにするために、連結リストを2段階にする
	//nextLevelArc,prevLevelArcはlevelごとの最初のエントリーにのみ保持される
	std::vector<int> nextLevelArc, nextPeerArc, prevLevelArc, prevPeerArc;	//Edge×2 → Edge×2+⊥
	//使いまわして使う一時的なもの。サイズはそれぞれM, N
	std::vector<bool> edgeVisited, vertexVisited;
	//internal nodeたち
	std::vector<Node> internalNodes;
	std::vector<Node*> internalNodeFreeList;

	bool isLocalRoot(const Node *a) const {
		return !a->parent || a->parent->mid == a;
	}

	void updateNode(Node *a) const {
		int v = getVertexIndex(a);
		int treeEdges = 0, nonTreeEdges = 0;
		if(a->left) {
			treeEdges |= a->left->treeEdges;
			nonTreeEdges |= a->left->nonTreeEdges;
		}
		if(a->mid) {
			treeEdges |= a->mid->treeEdges;
			nonTreeEdges |= a->mid->nonTreeEdges;
		}
		if(a->right) {
			treeEdges |= a->right->treeEdges;
			nonTreeEdges |= a->right->nonTreeEdges;
		}
		if(v != -1) {
			treeEdges |= levelTreeEdges[v];
			nonTreeEdges |= levelNonTreeEdges[v];
		}
		a->treeEdges = treeEdges;
		a->nonTreeEdges = nonTreeEdges;
	}

	//rotateはupdateしない
	void rotateR(Node *a) const {
		Node *q = a->parent, *r = q->parent;
		if(q->left = a->right) a->right->parent = q;
		a->right = q; q->parent = a;
		if(a->parent = r) {
			if(r->left == q) r->left = a;
			else if(r->right == q) r->right = a;
			else if(r->mid == q) r->mid = a;
		}
	}
	void rotateL(Node *a) const {
		Node *q = a->parent, *r = q->parent;
		if(q->right = a->left) a->left->parent = q;
		a->left = q; q->parent = a;
		if(a->parent = r) {
			if(r->left == q) r->left = a;
			else if(r->right == q) r->right = a;
			else if(r->mid == q) r->mid = a;
		}
	}

	//localなsplay
	void localSplay(Node *a) {
		//この時点でaはupdateされている必要がある。
		//ループ中aはupdateせず、最後に1度だけupdateする
		if(isLocalRoot(a)) return;
		do {
			Node *q = a->parent;
			if(isLocalRoot(q)) {
				if(q->left == a) {
					rotateR(a);
					updateNode(a->right);
				}else {
					rotateL(a);
					updateNode(a->left);
				}
			}else {
				Node *r = q->parent;
				if(r->left == q) {
					if(q->left == a) {
						rotateR(q), rotateR(a);
						updateNode(q->right), updateNode(q);
					}else {
						rotateL(a), rotateR(a);
						updateNode(a->left), updateNode(a->right);
					}
				}else {
					if(q->right == a) {
						rotateL(q), rotateL(a);
						updateNode(q->left), updateNode(q);
					}else {
						rotateR(a), rotateL(a);
						updateNode(a->left), updateNode(a->right);
					}
				}
			}
		}while(!isLocalRoot(a));
		updateNode(a);
	}

	Node *newNode() {
		assert(!internalNodeFreeList.empty());
		Node *p = internalNodeFreeList.back(); internalNodeFreeList.pop_back();
		new(p) Node();

		return p;
	}
	void freeNode(Node *&a) {
		assert(getVertexIndex(a) == -1);

		a->left = a->right = a->parent = a->mid = reinterpret_cast<Node*>(0xdeaddead);
		internalNodeFreeList.push_back(a);
		a = NULL;
	}

	//global tree上での親ノードを取得する
	Node *globalParent(Node *a) {
		localSplay(a);
		return a->parent;
	}
	//globalParentと同じだがsplayせずに取得する
	Node *globalParentNoSplay(Node *a) {
		while(!isLocalRoot(a)) a = a->parent;
		return a->parent;
	}
	//global tree上での根ノードを取得する
	Node *getRootNode(int u) {
		Node *a = &vertexNodes[u];
		while(a->parent) a = globalParent(a);
		return a;
	}
	//aからrootへのパスを更新する
	void updatePath(Node *a) {
		while(a) {
			updateNode(a);
			localSplay(a);
			a = a->parent;
		}
	}

	//local tree上でaを削除する(切り離す)。aのglobal tree上の親を返す。cutNodeなどで内部的に用いるためのもの
	Node *deleteNode(Node *a) {
		localSplay(a);
		Node *r = a->left, *p = a->parent;
		if(!r) {
			if(r = a->right) r->parent = a->parent;
			if(a->parent) a->parent->mid = r;
		}else {
			while(r->right) r = r->right;
			localSplay(r);
			assert(r->right == a && !a->left);
			Node *b = a->right;
			if(r->right = b) b->parent = r;
			updateNode(r);
		}
		a->left = a->right = a->parent = NULL;
		updateNode(a);
		return p;
	}
	//global tree上でaから親への辺をカットする
	void cutNode(Node *a) {
		int numLeafs = a->numLeafs;
		Node *b = deleteNode(a);
		while(b) {
			updateNode(b);
			b->numLeafs -= numLeafs;
			b = globalParent(b);
		}
	}
	//aが子,bが親としてglobal tree上でリンクする
	void linkNode(Node *a, Node *b) {
		assert(!a->left && !a->right && !a->parent);
		int numLeafs = a->numLeafs;
		a->parent = b;
		if(a->left = b->mid) a->left->parent = a;
		b->mid = a;
		updateNode(a);
		while(b) {
			updateNode(b);
			b->numLeafs += numLeafs;
			b = globalParent(b);
		}
	}

	//local search treeをマージする
	//親がいないように切り離してからmergeする必要がある
	void mergeNode(Node *a, Node *b) {
		assert(!a->parent && !a->left && !a->right && !b->parent && !b->left && !b->right);
		Node *c = newNode();
		if(c->left = a->mid) c->left->parent = c;
		if(c->right = b->mid) c->right->parent = c;
		c->parent = a, a->mid = c;
		updateNode(c);
		updateNode(a);
		deleteNode(c);
		freeNode(c);
		b->mid = NULL;
		a->numLeafs += b->numLeafs;
		freeNode(b);
	}

	//レベルがちょうどlvになるように、ノードが省略されている場合は新たに作って返す
	Node *getLevelNode(Node *a, int lv) {
		if(a->level == lv) return a;
		Node *b = newNode();
		if(a->level > lv) {
			Node *p = a->parent;
			if(p) {
				if(p->left == a) p->left = b;
				else if(p->right == a) p->right = b;
				else p->mid = b;
			}
			b->parent = p;
			b->mid = a; a->parent = b;
			if(b->left = a->left) b->left->parent = b;
			if(b->right = a->right) b->right->parent = b;
			a->left = a->right = NULL;
			updateNode(a);
			updateNode(b);
			b->numLeafs = a->numLeafs;
			b->level = lv;
		}else {
			assert(false);
		}
		return b;
	}

	//aの子が1つだけだったら無駄なのでcontractする。下のノードを残すようにする
	Node *contractOneChildNode(Node *a) {
		Node *b = a->mid;
		if(!(b && !b->left && !b->right)) return a;
		Node *p = a->parent;
		b->parent = p;
		if(p) {
			if(p->left == a) p->left = b;
			else if(p->right == a) p->right = b;
			else p->mid = b;
		}
		if(b->left = a->left) b->left->parent = b;
		if(b->right = a->right) b->right->parent = b;
		updateNode(b);
		freeNode(a);
		return b;
	}

	//edgeからarcを取得する。arcは向きがあるので2つに対応する
	int arc1(int ei) const { return ei; }
	int arc2(int ei) const { return ei + numMaxEdges(); }
	//arcからedgeを取得する
	int arcEdge(int i) const { return i >= numMaxEdges() ? i - numMaxEdges() : i; }

	//vはarcのtail
	void insertArcToList(int i, int v) {
		int lv = edgeLevel[arcEdge(i)];
		bool treeEdge = isTreeEdge[arcEdge(i)];
		std::vector<int> &first = treeEdge ? firstTreeArc : firstNonTreeArc;

		int it = first[v], prev = -1;
		while(it != -1 && edgeLevel[arcEdge(it)] < lv) {
			prev = it;
			it = nextLevelArc[it];
		}

		prevPeerArc[i] = -1;
		if((prevLevelArc[i] = prev) != -1) nextLevelArc[prev] = i;
		else first[v] = i;

		int nextPeer = -1, nextLevel = -1;
		if(it != -1 && edgeLevel[arcEdge(it)] == lv) {
			nextPeer = it;
			nextLevel = nextLevelArc[it];
			nextLevelArc[it] = prevLevelArc[it] = -1;
		}else {
			nextLevel = it;
			std::vector<int> &levelEdges = treeEdge ? levelTreeEdges : levelNonTreeEdges;
			levelEdges[v] |= 1 << lv;
		}
		if((nextPeerArc[i] = nextPeer) != -1) prevPeerArc[nextPeer] = i;
		if((nextLevelArc[i] = nextLevel) != -1) prevLevelArc[nextLevel] = i;
	}
	void removeArcFromList(int i, int v) {
		int lv = edgeLevel[arcEdge(i)];
		bool treeEdge = isTreeEdge[arcEdge(i)];
		std::vector<int> &first = treeEdge ? firstTreeArc : firstNonTreeArc;

		int nextPeer = nextPeerArc[i], prevPeer = prevPeerArc[i];
		if(prevPeer != -1) nextPeerArc[prevPeer] = nextPeer;
		if(nextPeer != -1) prevPeerArc[nextPeer] = prevPeer;
		nextPeerArc[i] = prevPeerArc[i] = -2;

		if(prevPeer == -1) {
			int nextLevel = nextLevelArc[i], prevLevel = prevLevelArc[i];
			if(nextPeer != -1) {
				nextLevelArc[nextPeer] = nextLevel;
				prevLevelArc[nextPeer] = prevLevel;
				if(prevLevel != -1) nextLevelArc[prevLevel] = nextPeer;
				else first[v] = nextPeer;
				if(nextLevel != -1) prevLevelArc[nextLevel] = nextPeer;
			}else {
				if(prevLevel != -1) nextLevelArc[prevLevel] = nextLevel;
				else first[v] = nextLevel;
				if(nextLevel != -1) prevLevelArc[nextLevel] = prevLevel;

				std::vector<int> &levelEdges = treeEdge ? levelTreeEdges : levelNonTreeEdges;
				levelEdges[v] &= ~(1 << lv);
			}
		}else assert(nextLevelArc[i] == -1 && prevLevelArc[i] == -1);
		nextLevelArc[i] = prevLevelArc[i] = -2;
	}

	void insertEdgeToListNoUpdate(int ei) {
		insertArcToList(arc1(ei), arcHead[arc2(ei)]);
		insertArcToList(arc2(ei), arcHead[arc1(ei)]);
	}
	void removeEdgeFromListNoUpdate(int ei) {
		removeArcFromList(arc1(ei), arcHead[arc2(ei)]);
		removeArcFromList(arc2(ei), arcHead[arc1(ei)]);
	}
	void updateEdgeStatus(int ei) {
		updatePath(&vertexNodes[arcHead[arc1(ei)]]);
		updatePath(&vertexNodes[arcHead[arc2(ei)]]);
	}

	//このノードがvertex nodeである場合頂点番号を返す。そうでない場合-1を返す
	int getVertexIndex(const Node *a) const {
		const Node *first = &vertexNodes[0];
		if(first <= a && a < first + numVertices())
			return a - first;
		else
			return -1;
	}

	template<bool TreeEdge>
	inline int getFirstLevelArc(int v, int lv) {
		int it = TreeEdge ? firstTreeArc[v] : firstNonTreeArc[v];
		while(it != -1 && edgeLevel[arcEdge(it)] < lv)
			it = nextLevelArc[it];
		if(it == -1 || edgeLevel[arcEdge(it)] != lv) return -1;
		return it;
	}
	//根だけは左右(left,right)には行けない
	template<bool TreeEdge>
	inline void enumArcAddRoot(Node *a, int lv, std::vector<Node*> &stk, std::vector<int> &arcs) {
		int Node::*edgeBitset = TreeEdge ? &Node::treeEdges : &Node::nonTreeEdges;
		if(a->mid && (a->mid->*edgeBitset >> lv & 1))
			stk.push_back(a->mid);
		int v = getVertexIndex(a);
		if(v != -1) {
			int it = getFirstLevelArc<TreeEdge>(v, lv);
			if(it != -1) arcs.push_back(it);
		}
	}
	//enumArcNextがまだできるかどうか
	inline bool enumArcContinue(const std::vector<Node*> &stk, std::vector<int> &arcs) {
		return !stk.empty() || !arcs.empty();
	}
	//tree/nontree arcsを1つずつ列挙する
	template<bool TreeEdge>
	inline int enumArcNext(int lv, std::vector<Node*> &stk, std::vector<int> &arcs) {
		if(!arcs.empty()) {
			int currentArc = arcs.back();
			int next = nextPeerArc[currentArc];
			if(next == -1) arcs.pop_back();
			else arcs.back() = next;
			return currentArc;
		}
		int Node::*edgeBitset = TreeEdge ? &Node::treeEdges : &Node::nonTreeEdges;
		const std::vector<int> &levelEdges = TreeEdge ? levelTreeEdges : levelNonTreeEdges;
		int u;
		do {
			Node *a = stk.back(); stk.pop_back();
			assert(a->*edgeBitset >> lv & 1);
			if(a->left && (a->left->*edgeBitset >> lv & 1))
				stk.push_back(a->left);
			if(a->mid && (a->mid->*edgeBitset >> lv & 1))
				stk.push_back(a->mid);
			if(a->right && (a->right->*edgeBitset >> lv & 1))
				stk.push_back(a->right);
			u = getVertexIndex(a);
		}while(u == -1 || !(levelEdges[u] >> lv & 1));
		//この頂点から出てるレベルlvの木辺を列挙
		//木辺の次数がとても大きい頂点があるとone stepがboundしなくなってしまうので、遅延評価的にstateで保存しておく
		int it = getFirstLevelArc<TreeEdge>(u, lv);
		assert(it != -1);
		assert(edgeLevel[arcEdge(it)] == lv);
		arcs.push_back(it);
		return enumArcNext<TreeEdge>(lv, stk, arcs);
	}


	//global node aの木上で(v,w)を切った時に残る2つの木のうちサイズが小さいものを見つけ、
	//その側ua∈{va,wa}とそれに属するglobal tree中のaの子たちを返す
	Node *findSmallerTree(Node *a, Node *va, Node *wa, std::vector<Node*> &uNodes, std::vector<int> &uEdges, int &uSize) {
		int lv = a->level;
		int vsize = va->numLeafs, wsize = wa->numLeafs;
		std::vector<Node*> stkv, stkw, vNodes, wNodes;
		std::vector<int> vArcs, wArcs, vEdges, wEdges, xEdges;

		enumArcAddRoot<true>(va, lv, stkv, vArcs);
		enumArcAddRoot<true>(wa, lv, stkw, wArcs);
		while(enumArcContinue(stkv, vArcs) && enumArcContinue(stkw, wArcs)) {
			if(vsize <= wsize)
				vsize += findSmallerTreeStep(lv, stkv, vArcs, vNodes, vEdges);
			else
				wsize += findSmallerTreeStep(lv, stkw, wArcs, wNodes, wEdges);
		}
		for(int i = 0; i < (int)vEdges.size(); i ++)
			edgeVisited[vEdges[i]] = false;
		for(int i = 0; i < (int)wEdges.size(); i ++)
			edgeVisited[wEdges[i]] = false;

		Node *ua;
		if(!enumArcContinue(stkv, vArcs)) {
			ua = va, uSize = vsize, uNodes.swap(vNodes);
			uEdges.swap(vEdges), xEdges.swap(wEdges);
		}else {
			ua = wa, uSize = wsize, uNodes.swap(wNodes);
			uEdges.swap(wEdges), xEdges.swap(vEdges);
		}
		return ua;
	}

	inline int findSmallerTreeStep(int lv, std::vector<Node*> &stk, std::vector<int> &arcs, std::vector<Node*> &nodes, std::vector<int> &edges) {
		int i = enumArcNext<true>(lv, stk, arcs);
		int ei = arcEdge(i);
		if(edgeVisited[ei]) return 0;
		edgeVisited[ei] = true;
		edges.push_back(ei);
		Node *b = &vertexNodes[arcHead[i]];
		while(true) {
			Node *p = globalParent(b);	//探索してるとこをsplayすることはないはず
			if(p->level <= lv) break;
			b = p;
		}
		assert(lv < b->level);
		enumArcAddRoot<true>(b, lv, stk, arcs);	//向こう側も探索
		nodes.push_back(b);
		return b->numLeafs;
	}

	//aのsubtreeにincidentしてるレベルlvのnontree edgeを1つサンプルする
	//ランダムにサンプルってできないかと思うのだけど…とりあえずランダムさは無くやる。構造自体をランダムにするとか?
	int sampleNonTreeEdge(Node *a, int lv) {
		int v = getVertexIndex(a);
		if(v == -1) {
			Node *b = a->mid;
			if(!(b->nonTreeEdges >> lv & 1)) return -1;
			while(true) {
				v = getVertexIndex(b);
				if(v != -1 && (levelNonTreeEdges[v] >> lv & 1))
					break;
				if(b->left && (b->left->nonTreeEdges >> lv & 1))
					b = b->left;
				else if(b->right && (b->right->nonTreeEdges >> lv & 1))
					b = b->right;
				else if(b->mid && (b->mid->nonTreeEdges >> lv & 1))
					b = b->mid;
				else assert(false);
			}
		}
		return getFirstLevelArc<false>(v, lv);
	}

	//aのレベルでvとwを連結させる辺を見つける。見つかったかどうかを返す
	//va,waはv,wの祖先でglobal tree上でのaの子ノード
	bool replaceRec(int lv, Node *a, Node *va, Node *wa) {
		//小さい方の木T_uを見つける
		std::vector<Node*> uNodes; std::vector<int> uEdges; int uSize;
		Node *ua_t = findSmallerTree(a, va, wa, uNodes, uEdges, uSize);
		Node *ua = getLevelNode(ua_t, lv + 1);

		//T_uをglobal treeの構造的にレベルアップさせる
		cutNode(ua);
		for(int i = 0; i < (int)uNodes.size(); i ++) {
			Node *n = getLevelNode(uNodes[i], lv + 1);
			cutNode(n);
			mergeNode(ua, n);
		}
		//uaはcutしたままにしておく

		int replacementEdge = -1;
		std::vector<int> levelupEdges;
		//T_uのこのレベルのtree edgesをレベルアップさせるためにlevelupEdgesに入れとく
		levelupEdges.swap(uEdges);

		//まず何個かサンプリングする
		for(int k = 1; k <= uSize; k <<= 1) {
			int i = sampleNonTreeEdge(ua, lv);
			if(i == -1) break;
			int ei = arcEdge(i);
			//lvまで登る
			Node *b = &vertexNodes[arcHead[i]];
			while(b->parent && b->level > lv + 1) {
				b = globalParent(b);
			}
			if(b == ua) {
				//両端がT_u側にある辺である
				//サンプリングのために今ここでレベルアップする
				removeEdgeFromListNoUpdate(ei);
				edgeLevel[ei] ++;
				insertEdgeToListNoUpdate(ei);
				updateEdgeStatus(ei);
			}else {
				//replacement edgeが見つかった
				replacementEdge = ei;
				break;
			}
		}

		int treeEdgeCount = levelupEdges.size();
		//Test-All的な
		if(replacementEdge == -1) {
			//replacement edgeは少ないと仮定してやる

			std::vector<Node*> stk; static std::vector<int> arcs;
			stk.clear(); arcs.clear();
			//このレベルのT_uのnon tree edgesを列挙する
			enumArcAddRoot<false>(ua, lv, stk, arcs);
			while(enumArcContinue(stk, arcs)) {
				int i = enumArcNext<false>(lv, stk, arcs);
				int ei = arcEdge(i);
				vertexVisited[arcHead[i == arc1(ei) ? arc2(ei) : arc1(ei)]] = true;
				if(edgeVisited[ei]) continue;
				edgeVisited[ei] = true;

				levelupEdges.push_back(ei);
			}

			for(int i = treeEdgeCount; i < (int)levelupEdges.size(); i ++) {
				int ei = levelupEdges[i];
				if(!vertexVisited[arcHead[arc1(ei)]] || !vertexVisited[arcHead[arc2(ei)]]) {
					replacementEdge = ei;
					break;
				}
			}
		}
		for(int i = 0; i < (int)levelupEdges.size(); i ++) {
			int ei = levelupEdges[i];
			if(treeEdgeCount <= i) {
				//replacement edgeじゃないならTest-Allで両方の向きが見つかってるはず
				if(!vertexVisited[arcHead[arc1(ei)]] || !vertexVisited[arcHead[arc2(ei)]]) {
					//replacement edgeはレベルアップしない(できない)
					//replacement edgeばかりの時にlevel upできなくてpayできないので最初にサンプリングしてる
					if(replacementEdge == -1) {
						replacementEdge = ei;
					}
					continue;
				}
			}
			//見たedgeを実際にレベルアップさせる
			removeEdgeFromListNoUpdate(ei);
			edgeLevel[ei] ++;
			insertEdgeToListNoUpdate(ei);
			updateEdgeStatus(ei);
		}
		for(int i = treeEdgeCount; i < (int)levelupEdges.size(); i ++) {
			int ei = levelupEdges[i];
			edgeVisited[ei] = false;
			vertexVisited[arcHead[arc1(ei)]] = false;
			vertexVisited[arcHead[arc2(ei)]] = false;
		}
		if(replacementEdge != -1) {
			//replacement edgeが見つかった場合
			removeEdgeFromListNoUpdate(replacementEdge);
			isTreeEdge[replacementEdge] = true;
			insertEdgeToListNoUpdate(replacementEdge);
			updateEdgeStatus(replacementEdge);
			linkNode(ua, a);	//uaを連結させる
			a = contractOneChildNode(a);
			ua = contractOneChildNode(ua);
			edgeVisited[replacementEdge] = false;
			return true;
		}
		//replacement edgeが見つからなかった場合
		Node *up = newNode();
		up->level = lv;
		linkNode(ua, up);
		a = contractOneChildNode(a);
		up = contractOneChildNode(up);
		if(up == ua) up = ua = contractOneChildNode(ua);
		else ua = contractOneChildNode(ua);
		if(lv > 0) {
			Node *ap = globalParent(a);
			Node *p = ap && ap->level == lv-1 ? ap : getLevelNode(a, lv-1);
			linkNode(up, p);
			return replaceRec(lv-1, p, up, a);
		}else {
			return false;
		}
	}

	//aとbを連結させる辺を見つける。見つかったかどうかを返す
	bool replace(Node *a, Node *b) {
		//v,wのglobal tree上のlcaを見つける。ap,bpにそのすぐ下の子を保持しておく
		Node *ap = NULL, *bp = NULL;
		assert(a != b);
		while(a != b) {
			assert(a && b);
			if(a->level >= b->level)
				ap = a, a = globalParent(a);
			else if(a->level < b->level)
				bp = b, b = globalParent(b);
		}
		return replaceRec(a->level, a, ap, bp);
	}

	//コピコンはとりあえずdelete
	FullyDynamicConnectivity(const FullyDynamicConnectivity &that) { assert(false); }
	FullyDynamicConnectivity &operator=(const FullyDynamicConnectivity &that) { assert(false); }

public:
	FullyDynamicConnectivity() { }
	~FullyDynamicConnectivity() { }

	//floor(log_2(N))
	static int calcMaxLevel(int N) {
		int maxLevel = 0;
		while(1 << maxLevel <= N / 2) maxLevel ++;
		return maxLevel;
	}

	//頂点数N, 最大辺数Mとして初期化する(edgeless graphとなる)
	void init(int N, int M) {
		internalNodeFreeList.clear();
		//子を2つ以上まとめるノードになるには辺が必要。その他に一時的なノード分として+α
		//よく考えてないのでだめかも。とにかくランダムケースではinternal nodeはとても少ない(だからテストが難しい)
		internalNodes.resize(M + 5);
		internalNodeFreeList.resize(internalNodes.size());
		for(int i = 0; i < (int)internalNodes.size(); i ++)
			internalNodeFreeList[i] = &internalNodes[i];

		int maxLevel = calcMaxLevel(N);

		vertexNodes.assign(N, Node());
		for(int i = 0; i < N; i ++) {
			vertexNodes[i].level = maxLevel;
			vertexNodes[i].numLeafs = 1;
		}
		
		isTreeEdge.assign(M, false);
		edgeLevel.assign(M, -1);

		arcHead.assign(M * 2, -1);

		levelTreeEdges.assign(N, 0);
		levelNonTreeEdges.assign(N, 0);

		firstTreeArc.assign(N, -1);
		firstNonTreeArc.assign(N, -1);

		nextLevelArc.assign(M * 2, -2);
		nextPeerArc.assign(M * 2, -2);
		prevLevelArc.assign(M * 2, -2);
		prevPeerArc.assign(M * 2, -2);

		edgeVisited.assign(M, false);
		vertexVisited.assign(N, false);
	}
	int numVertices() const { return vertexNodes.size(); }
	int numMaxEdges() const { return isTreeEdge.size(); }
	//uとvが連結しているかどうかを返す
	bool isConnected(int v, int w) {
		assert(0 <= v && v < numVertices() && 0 <= w && w < numVertices());
		return getRootNode(v) == getRootNode(w);
	}
	//vが属する連結成分のサイズを返す
	int getConnectedComponentSize(int v) {
		assert(0 <= v && v < numVertices());
		return getRootNode(v)->numLeafs;
	}
	//辺(u,v)を追加する。eiは辺のインデックスで、削除する時に指定する
	//connected componentsの数が減ったか(新たに連結されたか)を返す
	bool insertEdge(int ei, int v, int w) {
		assert(0 <= ei && ei < numMaxEdges() && 0 <= v && v < numVertices() && 0 <= w && w < numVertices());
		assert(edgeLevel[ei] == -1);

		Node *vRoot = getRootNode(v);
		Node *wRoot = getRootNode(w);
		bool treeEdge = vRoot != wRoot;

		isTreeEdge[ei] = treeEdge;
		edgeLevel[ei] = 0;

		int a1 = arc1(ei), a2 = arc2(ei);
		assert(arcHead[a1] == -1 && arcHead[a2] == -1);
		arcHead[a1] = w, arcHead[a2] = v;

		if(v != w) {	//ループは見たくないのでリストには登録しない
			insertEdgeToListNoUpdate(ei);
			updateEdgeStatus(ei);
		}

		if(treeEdge) {
			Node *v0 = getLevelNode(vRoot, 0), *w0 = getLevelNode(wRoot, 0);
			mergeNode(v0, w0);
		}
		return treeEdge;
	}
	//辺を削除する。eiは辺のインデックス
	//connected componentsの数が増えたか(新たに分離したか)を返す
	bool deleteEdge(int ei) {
		assert(0 <= ei && ei < numMaxEdges());
		assert(edgeLevel[ei] != -1);

		int a1 = arc1(ei), a2 = arc2(ei);
		int v = arcHead[a2], w = arcHead[a1];

		if(v != w) {	//ループは登録されていない
			removeEdgeFromListNoUpdate(ei);
			updateEdgeStatus(ei);
		}

		arcHead[a1] = arcHead[a2] = -1;

		bool treeEdge = isTreeEdge[ei];
		isTreeEdge[ei] = false;
		edgeLevel[ei] = -1;

		bool splited;
		if(!treeEdge) splited = false;
		else splited = !replace(&vertexNodes[v], &vertexNodes[w]);
		return splited;
	}
};

int main() {
	int N, M;
	scanf("%d%d", &N, &M);
	if((ll)N * (N-1) / 2 - M > N - 1) {
		rep(i, M) {
			int S, T;
			scanf("%d%d", &S, &T);
			puts("no");
		}
		return 0;
	}
	vector<vector<int> > edgeIndex(N);
	int totalEdges = 0;
	rep(i, N) {
		edgeIndex[i].resize(i);
		rep(j, i) edgeIndex[i][j] = totalEdges ++;
	}

	FullyDynamicConnectivity fdc;
	fdc.init(N, totalEdges);

	vector<bool> edgeExist(totalEdges);
	int numComponents = N, numEdges = 0;
	//最初に完全グラフにする
	rep(i, N) rep(j, i) {
		int ei = edgeIndex[i][j];
		numComponents -= fdc.insertEdge(ei, i, j);
		edgeExist[ei] = true;
		++ numEdges;
	}

	rep(i, M) {
		int S, T;
		scanf("%d%d", &S, &T), S --, T --;
		if(S < T) swap(S, T);
		int ei = edgeIndex[S][T];
		if(!edgeExist[ei]) {
			numComponents -= fdc.insertEdge(ei, S, T);
			edgeExist[ei] = true;
			++ numEdges;
		}else {
			numComponents += fdc.deleteEdge(ei);
			edgeExist[ei] = false;
			-- numEdges;
		}
//		cerr << "graph{"; rep(i, N) cerr << i << ";"; rep(i, N) rep(j, i) if(edgeExist[edgeIndex[i][j]]) cerr << i << "--" << j <<";"; cerr << "}" << endl;
//		cerr << N << " - " << numComponents << " == " << numEdges << endl;
		bool isForest = numEdges == N - numComponents;
		puts(isForest ? "yes" : "no");
	}
	return 0;
}

Submission Info

Submission Time
Task C - 森ですか?
User anta
Language C++ (G++ 4.6.4)
Score 100
Code Size 28331 Byte
Status AC
Exec Time 264 ms
Memory 11464 KB

Compile Error

./Main.cpp: In function ‘int main()’:
./Main.cpp:794:23: warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result [-Wunused-result]
./Main.cpp:798:25: warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result [-Wunused-result]
./Main.cpp:825:36: warning: ignoring return value of ‘int scanf(const char*, ...)’, declared with attribute warn_unused_result [-Wunused-result]

Judge Result

Set Name Easy All
Score / Max Score 50 / 50 50 / 50
Status
AC × 37
AC × 79
Set Name Test Cases
Easy easy_01_00.txt, easy_01_01.txt, easy_01_02.txt, easy_01_03.txt, easy_01_04.txt, easy_02_00.txt, easy_02_01.txt, easy_02_02.txt, easy_02_03.txt, easy_02_04.txt, easy_03_00.txt, easy_03_01.txt, easy_03_02.txt, easy_03_03.txt, easy_03_04.txt, easy_04_00.txt, easy_04_01.txt, easy_04_02.txt, easy_04_03.txt, easy_04_04.txt, easy_05_00.txt, easy_05_01.txt, easy_05_02.txt, easy_05_03.txt, easy_05_04.txt, easy_06_00.txt, easy_06_01.txt, easy_06_02.txt, easy_06_03.txt, easy_06_04.txt, easy_07_00.txt, easy_07_01.txt, easy_07_02.txt, easy_07_03.txt, easy_07_04.txt, easy_08_00.txt, easy_08_01.txt
All easy_01_00.txt, easy_01_01.txt, easy_01_02.txt, easy_01_03.txt, easy_01_04.txt, easy_02_00.txt, easy_02_01.txt, easy_02_02.txt, easy_02_03.txt, easy_02_04.txt, easy_03_00.txt, easy_03_01.txt, easy_03_02.txt, easy_03_03.txt, easy_03_04.txt, easy_04_00.txt, easy_04_01.txt, easy_04_02.txt, easy_04_03.txt, easy_04_04.txt, easy_05_00.txt, easy_05_01.txt, easy_05_02.txt, easy_05_03.txt, easy_05_04.txt, easy_06_00.txt, easy_06_01.txt, easy_06_02.txt, easy_06_03.txt, easy_06_04.txt, easy_07_00.txt, easy_07_01.txt, easy_07_02.txt, easy_07_03.txt, easy_07_04.txt, easy_08_00.txt, easy_08_01.txt, hard_01_00.txt, hard_01_01.txt, hard_01_02.txt, hard_01_03.txt, hard_01_04.txt, hard_02_00.txt, hard_02_01.txt, hard_02_02.txt, hard_02_03.txt, hard_02_04.txt, hard_03_00.txt, hard_03_01.txt, hard_03_02.txt, hard_03_03.txt, hard_03_04.txt, hard_04_00.txt, hard_04_01.txt, hard_04_02.txt, hard_04_03.txt, hard_04_04.txt, hard_05_00.txt, hard_05_01.txt, hard_05_02.txt, hard_05_03.txt, hard_05_04.txt, hard_06_00.txt, hard_06_01.txt, hard_06_02.txt, hard_06_03.txt, hard_06_04.txt, hard_07_00.txt, hard_07_01.txt, hard_07_02.txt, hard_07_03.txt, hard_07_04.txt, hard_08_00.txt, hard_08_01.txt, hard_09_00.txt, hard_09_01.txt, hard_09_02.txt, hard_09_03.txt, hard_09_04.txt
Case Name Status Exec Time Memory
easy_01_00.txt AC 176 ms 948 KB
easy_01_01.txt AC 28 ms 892 KB
easy_01_02.txt AC 26 ms 900 KB
easy_01_03.txt AC 27 ms 972 KB
easy_01_04.txt AC 27 ms 940 KB
easy_02_00.txt AC 25 ms 908 KB
easy_02_01.txt AC 27 ms 1012 KB
easy_02_02.txt AC 26 ms 1012 KB
easy_02_03.txt AC 24 ms 896 KB
easy_02_04.txt AC 23 ms 1016 KB
easy_03_00.txt AC 27 ms 1020 KB
easy_03_01.txt AC 24 ms 912 KB
easy_03_02.txt AC 26 ms 940 KB
easy_03_03.txt AC 26 ms 1040 KB
easy_03_04.txt AC 25 ms 948 KB
easy_04_00.txt AC 25 ms 924 KB
easy_04_01.txt AC 26 ms 892 KB
easy_04_02.txt AC 28 ms 948 KB
easy_04_03.txt AC 28 ms 944 KB
easy_04_04.txt AC 27 ms 956 KB
easy_05_00.txt AC 27 ms 952 KB
easy_05_01.txt AC 27 ms 972 KB
easy_05_02.txt AC 27 ms 964 KB
easy_05_03.txt AC 76 ms 952 KB
easy_05_04.txt AC 27 ms 1016 KB
easy_06_00.txt AC 25 ms 956 KB
easy_06_01.txt AC 27 ms 940 KB
easy_06_02.txt AC 26 ms 924 KB
easy_06_03.txt AC 26 ms 920 KB
easy_06_04.txt AC 27 ms 964 KB
easy_07_00.txt AC 23 ms 1012 KB
easy_07_01.txt AC 27 ms 952 KB
easy_07_02.txt AC 26 ms 1052 KB
easy_07_03.txt AC 27 ms 940 KB
easy_07_04.txt AC 27 ms 996 KB
easy_08_00.txt AC 29 ms 860 KB
easy_08_01.txt AC 27 ms 936 KB
hard_01_00.txt AC 180 ms 10648 KB
hard_01_01.txt AC 168 ms 6264 KB
hard_01_02.txt AC 168 ms 7708 KB
hard_01_03.txt AC 153 ms 3328 KB
hard_01_04.txt AC 149 ms 3228 KB
hard_02_00.txt AC 42 ms 1060 KB
hard_02_01.txt AC 45 ms 1168 KB
hard_02_02.txt AC 53 ms 1144 KB
hard_02_03.txt AC 53 ms 1300 KB
hard_02_04.txt AC 50 ms 1204 KB
hard_03_00.txt AC 180 ms 7936 KB
hard_03_01.txt AC 159 ms 3764 KB
hard_03_02.txt AC 203 ms 10152 KB
hard_03_03.txt AC 205 ms 7540 KB
hard_03_04.txt AC 191 ms 10576 KB
hard_04_00.txt AC 163 ms 6784 KB
hard_04_01.txt AC 54 ms 1148 KB
hard_04_02.txt AC 66 ms 2604 KB
hard_04_03.txt AC 36 ms 1072 KB
hard_04_04.txt AC 67 ms 3432 KB
hard_05_00.txt AC 199 ms 11260 KB
hard_05_01.txt AC 140 ms 4868 KB
hard_05_02.txt AC 168 ms 8960 KB
hard_05_03.txt AC 180 ms 10628 KB
hard_05_04.txt AC 188 ms 11312 KB
hard_06_00.txt AC 34 ms 1052 KB
hard_06_01.txt AC 36 ms 1076 KB
hard_06_02.txt AC 48 ms 1220 KB
hard_06_03.txt AC 68 ms 3464 KB
hard_06_04.txt AC 92 ms 2360 KB
hard_07_00.txt AC 233 ms 11428 KB
hard_07_01.txt AC 205 ms 10184 KB
hard_07_02.txt AC 264 ms 6908 KB
hard_07_03.txt AC 217 ms 9956 KB
hard_07_04.txt AC 208 ms 9596 KB
hard_08_00.txt AC 187 ms 11464 KB
hard_08_01.txt AC 51 ms 1148 KB
hard_09_00.txt AC 169 ms 7476 KB
hard_09_01.txt AC 139 ms 5548 KB
hard_09_02.txt AC 123 ms 5124 KB
hard_09_03.txt AC 143 ms 6400 KB
hard_09_04.txt AC 126 ms 4916 KB
sample1.txt AC 25 ms 900 KB
sample2.txt AC 28 ms 940 KB