zoukankan      html  css  js  c++  java
  • 题解【[AHOI2013]连通图】

    [ exttt{Description} ]

    给一个 (n) 个点,(m) 条边的无向图。

    (k) 次询问,每次询问给出一个边集。问:删去边集中的边后,改图是否为连通图。询问互相独立。

    允许离线。

    [ exttt{Solution} ]

    • 线段树分治好题。

    • 任意删边显然是不好做的,我们还是考虑把删边转化为加边。

    • 我们将询问的序列看成一个时间轴,我们会发现:每条边会在若干个时间区间内出现。

    • 具体地,我们考虑任意一条边 (x) ,设 (x) 边被删除 (w) 次,删除的位置分别是 (P_1,P_2,...,P_w) ,那么我们可以视为:(x) 边在时间区间 ([1,P_1-1],[P_1+1,P_2-1],...,[P_w+1,k]) 中出现。

    • 我们发现,像这种 " 操作在若干个时间区间内有效 " 的问题,恰恰是线段树分治易于解决的。

    • 离线处理出每条边被删除的位置,解决这个可以用 (m)STL vector 来解决。

    • 对于每一条边,将其挂在 " 该边出现的时间区间对应的线段树节点 " 的 STL vector 上。

    • 最后,我们跑一边整棵线段树,进行相应的操作。操作的过程我们可以使用 " 可撤销并查集 " 维护。

    • 再在并查集上维护一下 " 当前连通块大小((size)​)" ,对于任意加进去的边,若加边之后的连通块大小为 (n) ,则说明当前图为连通图(否则还有 (n-size) 个节点不在当前连通块中)。

    • 为了不破坏并查集形态,优化选择使用 " 按秩合并 "

    • (mathcal{O(kc log klog n)})评测链接

    [ exttt{Code} ]

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #include<stack>
    
    #define RI register int
    
    using namespace std;
    
    namespace IO
    {
        static char buf[1<<20],*fs,*ft;
        inline char gc()
        {
            if(fs==ft)
            {
    			ft=(fs=buf)+fread(buf,1,1<<20,stdin);
    			if(fs==ft)return EOF;
            }
            return *fs++;
        }
        #define gc() getchar()
    	inline int read()
    	{
    		int x=0,f=1;char s=gc();
    		while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
    		while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
    		return x*f;
    	}
    }using IO::read;
    
    const int N=100100,M=200100;
    
    int n,m;
    
    int a[M],b[M];
    
    int k;
    
    vector<int>pos[M];
    
    struct SegmentTree{
    	int l,r;
    	vector<int>e;
    }t[N*4];
    
    void build(int p,int l,int r)
    {
    	t[p].l=l,t[p].r=r;
    	if(l==r)return;
    	int mid=(l+r)/2;
    	build(p*2,l,mid),build(p*2+1,mid+1,r);
    }
    
    void insert(int p,int l,int r,int x)
    {
    	if(l<=t[p].l&&t[p].r<=r)
    	{
    		t[p].e.push_back(x);
    		return;
    	}
    	int mid=(t[p].l+t[p].r)/2;
    	if(l<=mid)
    		insert(p*2,l,r,x);
    	if(mid<r)
    		insert(p*2+1,l,r,x); 
    }
    
    int fa[N],size[N],d[N]; 
    
    int get(int x)
    {
    	while(fa[x]!=x)
    		x=fa[x];
    	return x;
    }
    
    struct Node{
    	int x,y,z;
    	Node(int v1,int v2,int v3):x(v1),y(v2),z(v3){}
    };
    
    stack<Node>s;
    
    void solve(int x,bool flag)
    {
    	int ori=s.size();
    
    	for(RI i=0;i<(int)t[x].e.size();i++)
    	{
    		int u=a[t[x].e[i]],v=b[t[x].e[i]];
    		int p=get(u),q=get(v);
    
    		if(p==q)continue;
    
    		if(d[p]<d[q])
    			swap(p,q);
    
    		s.push((Node){q,d[p]==d[q],size[q]});
    
    		fa[q]=p,d[p]+=d[p]==d[q],size[p]+=size[q];
    
    		if(size[p]==n)
    			flag=true;
    	}
    
    	if(t[x].l==t[x].r)
    		puts(flag?"Connected":"Disconnected");
    	else
    		solve(x*2,flag),solve(x*2+1,flag);
    
    	while(s.size()>ori)
    	{
    		Node res=s.top();s.pop();
    		d[fa[res.x]]-=res.y,size[fa[res.x]]-=res.z,fa[res.x]=res.x;
    	}
    }
    
    int main()
    {
    	n=read(),m=read();
    
    	for(RI i=1;i<=m;i++)
    		a[i]=read(),b[i]=read();
    
    	k=read();
    
    	for(RI i=1;i<=m;i++)
    		pos[i].push_back(0);
    
    	for(RI i=1;i<=k;i++)
    		for(RI c=read();c;c--)
    			pos[read()].push_back(i);
    
    	for(RI i=1;i<=m;i++)
    		pos[i].push_back(k+1);
    
    	build(1,1,k);
    
    	for(RI i=1;i<=m;i++)
    		for(RI j=1;j<(int)pos[i].size();j++)
    			if(pos[i][j-1]+1<=pos[i][j]-1)
    				insert(1,pos[i][j-1]+1,pos[i][j]-1,i);
    
    	for(RI i=1;i<=n;i++)
    		fa[i]=i,size[i]=1,d[i]=1;
    
    	solve(1,false);
    
    	return 0;
    }
    

    [ exttt{Thanks} exttt{for} exttt{watching} ]

  • 相关阅读:
    Linux常用命令大全
    CentOS安装Apche+Mysql+PHP
    ThinkPHP5.1设置404页面
    ThinkPHP5 循环标签
    deepin下安装apache+php+mysql
    deepin安装
    PHP中的http协议
    JSP四个作用域
    application跟session的区别
    jsp内置对象--session
  • 原文地址:https://www.cnblogs.com/cjtcalc/p/12454145.html
Copyright © 2011-2022 走看看