zoukankan      html  css  js  c++  java
  • luogu P5227 [AHOI2013]连通图 |线段树分治

    题目描述

    给定一个无向连通图和若干个小集合,每个小集合包含一些边,对于每个集合,你需要确定将集合中的边删掉后改图是否保持联通。集合间的询问相互独立

    定义一个图为联通的当且仅当对于任意的两个顶点,都存在一条路径连接它们

    输入格式

    第一行为两个整数 (n,m),代表无向图的点数和边数

    下面 (m) 行,包含两个整数 (u,v),代表该边连接点 (u,v)。第 (i + 1) 行的边的编号为 (i)。保证不存在重边和自环

    下面一行包含一个整数 (k),表示集合个数

    下面 (k) 行每行描述一个集合,每行的第一个数为集合中边的个数 (c),后面 (c) 个数代表集合内的边

    输出格式

    对于每个集合,输出一行代表去掉该集合中的边后图是否联通,如果联通输出 Connected,否则输出 Disconnected


    #include<stack>
    #include<vector>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N=2e5+5,M=2*N;
    inline int read(){
    	int x=0; char c=getchar();
    	while(c<'0'||c>'9')c=getchar();
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48); c=getchar();}
    	return x;
    }
    struct cnm{
    	int *x,y,*u,v;
    };
    stack<cnm>Q;
    int fa[N],rnk[N],now;
    int get(int x){
    	while(x^fa[x])x=fa[x];
    	return x;
    }
    void merge(int x,int y){
    	x=get(x),y=get(y);
    	if(x==y)return;
    	now--;
    	if(rnk[x]>rnk[y])swap(x,y);
    	Q.push((cnm){fa+x,fa[x],rnk+y,rnk[y]});
    	fa[x]=y; rnk[y]+=(rnk[x]==rnk[y]);
    }
    void undo(){
    	*Q.top().x=Q.top().y;
    	*Q.top().u=Q.top().v;
    	Q.pop(); now++;
    }
    int n,m,k;
    struct node{
    	int u,v;
    }e[N];
    vector<int>can[N];
    vector<node>vec[N<<1];
    #define ls p<<1
    #define rs p<<1|1
    #define mid ((l+r)>>1)
    void update(int p,int l,int r,int L,int R,node d){
    	if(L<=l&&r<=R){ vec[p].push_back(d); return; }
    	if(L<=mid)update(ls,l,mid,L,R,d);
    	if(R>mid)update(rs,mid+1,r,L,R,d);
    }
    void query(int p,int l,int r){
    	for(int i=0;i<vec[p].size();i++)merge(vec[p][i].u,vec[p][i].v);
    	if(l==r){ now==1?puts("Connected"):puts("Disconnected"); return; }
    	int sum=Q.size();
    	query(ls,l,mid);
    	while(Q.size()>sum)undo();
    	query(rs,mid+1,r);
    }
    signed main(){
    	n=read(),m=read();
    	for(int i=1;i<=n;i++)fa[i]=i; now=n;
    	for(int i=1;i<=m;i++)e[i].u=read(),e[i].v=read(),can[i].push_back(0);
    	k=read();
    	for(int i=1,x,y;i<=k;i++){
    		x=read();
    		while(x--){
    			y=read();
    			can[y].push_back(i);
    		}
    	}
    	for(int i=1;i<=m;i++)can[i].push_back(k+1);
    	
    	for(int i=1;i<=m;i++)
    	for(int j=0;j<can[i].size()-1;j++){
    		if(can[i][j]+1<=can[i][j+1]-1)
    		update(1,1,k,can[i][j]+1,can[i][j+1]-1,e[i]);
    	}
    	query(1,1,k);
    }
    
  • 相关阅读:
    《Linux内核分析》读书笔记(四章)
    LINUX内核分析第七周学习总结
    linux内核分析第六周学习笔记
    《Linux内核分析》第13章
    《Linux内核分析》实践4
    《Linux内核分析》实践3
    《Linux内核分析》实践2
    《Linux内核设计》第17章学习笔记
    实践指导1
    《Linux内核分析》期终总结&《Linux及安全》期中总结
  • 原文地址:https://www.cnblogs.com/naruto-mzx/p/13041952.html
Copyright © 2011-2022 走看看