zoukankan      html  css  js  c++  java
  • [SDOI2008] 洞穴勘测

    前言

    在我做这道题之前,我不会线段树分治和带撤销并查集,但是卷爷一下子就给我讲懂了。

    也许做 岛屿探险 的时候我自己发明了线段树分治?

    反正我现在学懂了。

    题目

    洛谷

    讲解

    LCT 板子题。

    好吧,由于蒟蒻笔者不会 LCT,就只能换个角度思考问题。

    检查连通性我们可以想到优秀的数据结构:并查集,而带删除我们可以想到带撤销并查集

    但是有个问题,带撤销并查集必须是按类似栈的顺序撤销并查集中的边才行,而不能是按照题目中的操作顺序。

    这可咋整?

    于是我们想到线段树分治

    对于每条边,我们可以将其出现过的时间找出来,可以发现这一定是个区间。

    然后将这条边加入线段树中,可以发现时间和空间都是 (log_2m) 级别的,可以接受。

    最后直接 dfs 整棵线段树即可。每走到一个节点,将这个节点上的所有边加入并查集,退出这个点的时候将这些边撤销掉。

    我们可以发现这个做法对于并查集来说,是按照加边顺序倒序撤销,正确。

    而对于询问来说,我们由于用线段树的区间代替时间,直接 dfs,时间顺序上也是正确的。

    时间复杂度 (O(mlog_2mlog_2n)),但由于其常数极小,所以可以与一个 (log) 的 LCT 相媲美。

    当然我说的常数小是相对于 LCT 来说的。

    代码

    //12252024832524
    #include <map>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXN = 200005;
    int n,m;
    char opt[10];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int f[MAXN],siz[MAXN];
    
    struct node
    {
    	int u,v,l,r;
    	bool f;
    	node(){}
    	node(int u1,int v1,int l1,int r1,bool f1){
    		u = u1;
    		v = v1;
    		l = l1;
    		r = r1;
    		f = f1;
    	}
    };
    #define lc (x<<1)
    #define rc (x<<1|1)
    vector<node> rt;
    map<pair<int,int>,int> ma;
    
    int s[MAXN][2],t;
    int findSet(int x)
    {
    	if(f[x] ^ x) return findSet(f[x]);
    	return x;
    }
    void unionSet(int u,int v)
    {
    	int U = findSet(u),V = findSet(v);
    	if(U == V) return;
    	if(siz[U] > siz[V]) swap(U,V);
    	f[U] = V; siz[V] += siz[U];
    	++t;
    	s[t][0] = U,s[t][1] = V;
    }
    void dfs(int x,int l,int r,vector<node> &now)
    {
    	int tmp = t,mid = (l+r) >> 1;
    	vector<node> L,R;
    	for(int i = 0,len = now.size();i < len;++ i)
    	{
    		if(now[i].l <= l && r <= now[i].r)//完全覆盖 
    		{
    			if(!now[i].f) unionSet(now[i].u,now[i].v);
    		}
    		else
    		{
    			if(now[i].l <= mid) L.push_back(now[i]);
    			if(mid+1 <= now[i].r) R.push_back(now[i]);
    		}
    	}
    	if(l == r)
    	{
    		for(int i = 0,len = now.size();i < len;++ i)
    			if(now[i].f)
    			{
    				if(findSet(now[i].u) == findSet(now[i].v)) printf("Yes
    ");
    				else printf("No
    ");
    			}
    	}
    	else dfs(lc,l,mid,L),L.clear(),dfs(rc,mid+1,r,R),R.clear();
    	now.clear();
    	for(int i = t;i > tmp;-- i) siz[s[i][1]] -= siz[s[i][0]],f[s[i][0]] = s[i][0];
    	t = tmp;
    }
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= n;++ i) f[i] = i,siz[i] = 1;
    	for(int i = 1,u,v;i <= m;++ i)
    	{
    		scanf("%s",opt); u = Read(),v = Read();
    		if(u > v) swap(u,v);
    		if(opt[0] == 'Q') rt.push_back(node(u,v,i,i,1));
    		else if(opt[0] == 'C') ma[make_pair(u,v)] = i;
    		else rt.push_back(node(u,v,ma[make_pair(u,v)],i-1,0)),ma[make_pair(u,v)] = 0;
    	}
    	for(map<pair<int,int>,int>::iterator it = ma.begin();it != ma.end();++ it)//记得剩下的边要加进去 
    		if(it->second)
    			rt.push_back(node((it->first).first,(it->first).second,it->second,m,0));
    	dfs(1,1,m,rt);
    	return 0;
    }
    

    后记

    一道题学懂线段树分治和带撤销并查集两个东西,血赚。

  • 相关阅读:
    网站安全编程 黑客入侵 脚本黑客 高级语法入侵 C/C++ C# PHP JSP 编程
    【算法导论】贪心算法,递归算法,动态规划算法总结
    cocoa2dx tiled map添加tile翻转功能
    8月30日上海ORACLE大会演讲PPT下载
    【算法导论】双调欧几里得旅行商问题
    Codeforces Round #501 (Div. 3) B. Obtaining the String (思维,字符串)
    Codeforces Round #498 (Div. 3) D. Two Strings Swaps (思维)
    Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle (数学,区间)
    洛谷 P1379 八数码难题 (BFS)
    Educational Codeforces Round 89 (Rated for Div. 2) A. Shovels and Swords (贪心)
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15151029.html
Copyright © 2011-2022 走看看