• 【bzoj3514】Codechef MARCH14 GERALD07加强版 LCT+可持久化线段树


    题目描述

    N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

    输入

    第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
    接下来M行,代表图中的每条边。
    接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。

    输出

    K行每行一个整数代表该组询问的联通块个数。

    样例输入

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

    样例输出

    2
    1
    3
    1


    题解

    LCT+可持久化线段树

    首先考虑离线怎么做:

    考虑按照时间顺序加入每一条边k的过程所带来的影响:当加入一条边时,

    如果这两个点原来不连通,则加入这条边后这两个点连通。故左端点在[1,k],右端点在k以后的询问中,这两个点都是连通的。所以把左端点[1,k]范围内的边数+1.

    如果这两个点原来是连通的,则加入这条边后会形成一个环。我们考虑这个环上的出现时刻最早的边,不需要时间早于该点即可是这个环上所有点连通。仔细推一推可以发现相当于把左端点[最早时刻,k]范围内的边数+1。

    把询问按照右端点时间排序,那么要做的就是:(1)维护环上出现时刻最早的边:使用LCT维护出现时间的最大生成树,并把边权转化为点权处理(a<->b变为a<->c<->b);(2)支持区间修改、单点查询。使用线段树即可。

    那么如果是强制在线呢?使用可持久化线段树,一个版本的可持久化线段树的每个节点的含义是:左端点在当前节点,右端点为该版本的询问的答案。每次需要再原版本线段树的基础上进行区间修改。这里使用可标记永久化的方式来维护。

    所以对于询问[l,r]直接在r版本的可持久化线段树中查询l节点的值即为当前生成森林的边数,使用n减去该数即为连通块数。

    时间复杂度$O(nlog n)$,常数巨大。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 400010
    using namespace std;
    int px[N >> 1] , py[N >> 1] , fa[N] , c[2][N] , mn[N] , rev[N] , ls[N * 40] , rs[N * 40] , sum[N * 40] , tot , root[N >> 1];
    inline void pushup(int x)
    {
    	mn[x] = min(x , min(mn[c[0][x]] , mn[c[1][x]]));
    }
    inline void pushdown(int x)
    {
    	if(rev[x])
    	{
    		int l = c[0][x] , r = c[1][x];
    		swap(c[0][l] , c[1][l]) , swap(c[0][r] , c[1][r]);
    		rev[l] ^= 1 , rev[r] ^= 1 , rev[x] = 0;
    	}
    }
    inline bool isroot(int x)
    {
    	return c[0][fa[x]] != x && c[1][fa[x]] != x;
    }
    void update(int x)
    {
    	if(!isroot(x)) update(fa[x]);
    	pushdown(x);
    }
    inline void rotate(int x)
    {
    	int y = fa[x] , z = fa[y] , l = (c[1][y] == x) , r = l ^ 1;
    	if(!isroot(y)) c[c[1][z] == y][z] = x;
    	fa[x] = z , fa[y] = x , fa[c[r][x]] = y , c[l][y] = c[r][x] , c[r][x] = y;
    	pushup(y) , pushup(x);
    }
    inline void splay(int x)
    {
    	int y , z;
    	update(x);
    	while(!isroot(x))
    	{
    		y = fa[x] , z = fa[y];
    		if(!isroot(y)) rotate((c[0][y] == x) ^ (c[0][z] == y) ? x : y);
    		rotate(x);
    	}
    }
    inline void access(int x)
    {
    	int t = 0;
    	while(x) splay(x) , c[1][x] = t , pushup(x) , t = x , x = fa[x];
    }
    inline int find(int x)
    {
    	while(fa[x]) x = fa[x];
    	return x;
    }
    inline void makeroot(int x)
    {
    	access(x) , splay(x) , swap(c[0][x] , c[1][x]) , rev[x] ^= 1;
    }
    inline void link(int x , int y)
    {
    	makeroot(x) , fa[x] = y;
    }
    inline void split(int x , int y)
    {
    	makeroot(x) , access(y) , splay(y);
    }
    inline void cut(int x , int y)
    {
    	split(x , y) , fa[x] = c[0][y] = 0 , pushup(y);
    }
    void update(int b , int e , int l , int r , int x , int &y)
    {
    	y = ++tot , sum[y] = sum[x] , ls[y] = ls[x] , rs[y] = rs[x];
    	if(b <= l && r <= e)
    	{
    		sum[y] ++ ;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(b <= mid) update(b , e , l , mid , ls[x] , ls[y]);
    	if(e > mid) update(b , e , mid + 1 , r , rs[x] , rs[y]);
    }
    int query(int p , int l , int r , int x)
    {
    	if(l == r) return sum[x];
    	int mid = (l + r) >> 1;
    	if(p <= mid) return sum[x] + query(p , l , mid , ls[x]);
    	else return sum[x] + query(p , mid + 1 , r , rs[x]);
    }
    int main()
    {
    	int n , m , k , type , i , x , y , last = 0;
    	scanf("%d%d%d%d" , &n , &m , &k , &type);
    	for(i = 1 ; i <= m + n ; i ++ ) mn[i] = i;
    	mn[0] = 1 << 30;
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		scanf("%d%d" , &px[i] , &py[i]) , px[i] += m , py[i] += m;
    		if(px[i] == py[i]) root[i] = root[i - 1];
    		else if(find(px[i]) != find(py[i])) link(px[i] , i) , link(py[i] , i) , update(1 , i , 1 , m , root[i - 1] , root[i]);
    		else
    		{
    			split(px[i] , py[i]) , x = mn[py[i]] , cut(px[x] , x) , cut(py[x] , x);
    			link(px[i] , i) , link(py[i] , i) , update(x + 1 , i , 1 , m , root[i - 1] , root[i]);
    		}
    	}
    	while(k -- )
    	{
    		scanf("%d%d" , &x , &y);
    		if(type) x ^= last , y ^= last;
    		printf("%d
    " , last = n - query(x , 1 , m , root[y]));
    	}
    	return 0;
    }
    

     

  • 相关阅读:
    Spark学习之路 (五)Spark伪分布式安装
    Spark学习之路 (四)Spark的广播变量和累加器
    Spark学习之路 (三)Spark之RDD
    Spark学习之路 (二)Spark2.3 HA集群的分布式安装
    Spark学习之路 (一)Spark初识
    通俗理解梯度下降
    通俗理解线性回归(二)
    通俗理解线性回归(一)
    python机器学习手写算法系列——线性回归
    CSS中display对布局的影响以及元素display的默认值
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7659640.html
Copyright © 2020-2023  润新知