zoukankan      html  css  js  c++  java
  • 题解 P4334 【[COI2007] Policija】

    题目链接

    Solution [COI2007] Policija

    题目大意:给定一张无向图,多次询问删去一个点或一条边后,(u,v)是否连通

    双连通分量,缩点


    分析:

    问题可以分成两个部分


    Subtask1: 删去一条边之后,查询 (u,v) 是否连通

    删去边 ((G_1,G_2)),只有 ((G_1,G_2)) 为割边,(u,v)才有可能不连通,这启示我们按照边双连通分量进行缩点

    缩点之后的树,每一条树边都对应着一条割边,每个点都对应着一个边双连通分量,问题变成:给定一棵树,删除一条边,询问 (u,v) 是否连通

    这个可以用dfs序解决,随便找个点为根,假设 $ G_1$ 的深度比 (G_2) 深,那么只要 (u,v)有一点在子树 (G_1) 内,另一点在外面就无法连通,否则可以

    这个还是比较naive的

    Subtask 2:删去一个点之后,查询 (u,v) 是否连通

    同上,我们按照点双连通分量进行缩点

    缩点之后的树,每一个点对应着原图割点,或者是去除割点之后的点双连通分量,问题变成: 给定一棵树,删除一个点 (c) ,询问 (u,v) 是否连通

    仍然可以用dfs序解决,(u,v) 一个点在子树 (c) 内,另一个在外是不连通的。

    除此之外还有一个特殊情况:如果 (c)(u,v) 的lca,那么也是不连通的

    倍增貌似会爆空间?用的树剖实现lca

    代码用namespace来分割subtask,分割原图和树,虽然略显冗余但是比较清楚且不容易锅?

    #include <cstdio>
    #include <cctype>
    #include <map>
    #include <queue>
    #include <stack>
    using namespace std;
    const int maxn = 1e5 + 100,maxm = 1e6 + 100;
    inline int read(){
    	int x = 0;char c = getchar();
    	while(!isdigit(c))c = getchar();
    	while(isdigit(c))x = x * 10 + c - '0',c = getchar();
    	return x;
    }
    int n,m;
    namespace task1{
    	int col_tot;
    	namespace graph{
    		vector<int> G[maxn];
    		inline void addedge(int u,int v){
    		G[u].push_back(v);}
    		int dep[maxn],siz[maxn],dfn[maxn],dfs_tot;
    		inline void dfs(int u,int faz = -1){
    			dep[1] = siz[u] = 1;
    			dfn[u] = ++dfs_tot;
    			for(int v : G[u]){
    				if(v == faz)continue;
    				dep[v] = dep[u] + 1;
    				dfs(v,u);
    				siz[u] += siz[v];
    			}
    		}
    		inline bool query(int a,int b,int x,int y){//x,y:road
    			if(dep[a] < dep[b])swap(a,b);
    			if(dep[x] < dep[y])swap(x,y);
    			if((dfn[a] >= dfn[x] && dfn[a] <= dfn[x] + siz[x] - 1) && (dfn[b] < dfn[x] || dfn[b] >= dfn[x] + siz[x]))return false;
    			if((dfn[a] < dfn[x] || dfn[a] > dfn[x] + siz[x] - 1) && (dfn[b] >= dfn[x] && dfn[b] < dfn[x] + siz[x]))return false;
    			return true;
    		}
    	}
    	struct edge{
    		int u,v;
    	}edges[maxm];
    	int head[maxn],nxt[maxm],tot = 1;
    	inline void addedge(int u,int v){
    		edges[++tot] = edge{u,v};
    		nxt[tot] = head[u];
    		head[u] = tot;
    	}
    	int dfn[maxn],low[maxn],iscut[maxm],dfs_tot;
    	inline void tarjan(int u,int faz = -1){
    		dfn[u] = low[u] = ++dfs_tot;
    		for(int i = head[u];i;i = nxt[i]){
    			if((i ^ 1) == faz)continue;
    			int v = edges[i].v;
    			if(!dfn[v]){
    				tarjan(v,i);
    				low[u] = min(low[u],low[v]);
    				if(low[v] > dfn[u])iscut[i] = iscut[i ^ 1] = 1;		
    			}else low[u] = min(low[u],dfn[v]);
    		}
    	}
    	int col[maxn];
    	inline void bfs(int s,int c){
    		queue<int> q;
    		col[s] = c;
    		q.push(s);
    		while(!q.empty()){
    			int u = q.front();q.pop();
    			for(int i = head[u];i;i = nxt[i]){
    				if(iscut[i])continue;
    				int v = edges[i].v;
    				if(col[v])continue;
    				col[v] = c;
    				q.push(v);
    			}
    		}
    	}
    	inline void init(){
    		for(int i = 1;i <= n;i++)
    			if(!dfn[i])tarjan(i);
    		for(int i = 1;i <= n;i++)
    			if(!col[i])bfs(i,++col_tot);
    		for(int i = 2;i <= tot;i++){
    			int u = edges[i].u,v = edges[i].v;
    			if(col[u] != col[v])graph::addedge(col[u],col[v]);
    		}
    		graph::dfs(1);
    	}
    	inline bool query(int a,int b,int u,int v){
    		if(col[u] == col[v])return true;
    		return graph::query(col[a],col[b],col[u],col[v]);
    	}
    }
    namespace task2{
    	namespace graph{
    		vector<int> G[maxn << 1];
    		inline void addedge(int u,int v){G[u].push_back(v);}
    		const int maxdep = 25;
    		int dfn[maxn << 1],siz[maxn << 1],dep[maxn << 1],top[maxn << 1],faz[maxn << 1],son[maxn << 1],dfs_tot;
    		inline void dfs1(int u){
    			dfn[u] = ++dfs_tot;
    			dep[1] = siz[u] = 1;
    			for(int v : G[u]){
    				if(v == faz[u])continue;
    				dep[v] = dep[u] + 1;
    				faz[v] = u;
    				dfs1(v);
    				if(siz[v] > siz[son[u]])son[u] = v;
    				siz[u] += siz[v];
    			}
    		}
    		inline void dfs2(int u,int tp){
    			top[u] = tp;
    			if(son[u])dfs2(son[u],tp);
    			for(int v : G[u]){
    				if(v == faz[u] || v == son[u])continue;
    				dfs2(v,v);
    			}
    		}
    		inline int lca(int x,int y){
    			while(top[x] != top[y]){
    				if(dep[top[x]] > dep[top[y]])x = faz[top[x]];
    				else y = faz[top[y]];
    			}
    			if(dep[x] < dep[y])return x;
    			else return y;
    		}
    		inline int query(int a,int b,int c){
    			if(dep[a] < dep[b])swap(a,b);
    			if((dfn[a] >= dfn[c] && dfn[a] <= dfn[c] + siz[c] - 1) && (dfn[b] < dfn[c] || dfn[b] >= dfn[c] + siz[c]))return false;
    			if((dfn[a] < dfn[c] || dfn[a] > dfn[c] + siz[c] - 1) && (dfn[b] >= dfn[c] && dfn[b] < dfn[c] + siz[c]))return false;
    			if(lca(a,b) == c)return false;
    			return true;
    		}
    	}
    	vector<int> G[maxn],dcc[maxn];
    	inline void addedge(int u,int v){G[u].push_back(v);}
    	int dfn[maxn],low[maxn],iscut[maxn],col[maxn],dfs_tot,dcc_tot;
    	stack<int> stk;
    	inline void tarjan(int u){
    		dfn[u] = low[u] = ++dfs_tot;
    		stk.push(u);
    		int child = 0;
    		for(int v : G[u]){
    			if(!dfn[v]){
    				tarjan(v);
    				child++;
    				low[u] = min(low[u],low[v]);
    				if(low[v] >= dfn[u]){
    					dcc_tot++;
    					iscut[u] = 1;
    					int t;
    					do{
    						t = stk.top();stk.pop();
    						dcc[dcc_tot].push_back(t);
    						col[t] = dcc_tot;
    					}while(t != v);
    					dcc[dcc_tot].push_back(u);
    				}
    			}else low[u] = min(low[u],dfn[v]);
    		}
    		if(u == 1 && child == 1)iscut[u] = 0;
    	}
    	inline void init(){
    		for(int i = 1;i <= n;i++)
    			if(!dfn[i])tarjan(i);
    		int num = dcc_tot;
    		for(int i = 1;i <= n;i++)
    			if(iscut[i])col[i] = ++dcc_tot;
    		for(int u = 1;u <= num;u++)
    			for(int v : dcc[u])
    				if(iscut[v])graph::addedge(u,col[v]),graph::addedge(col[v],u);
    		graph::dfs1(1);
    		graph::dfs2(1,1);
    	}
    	inline bool query(int a,int b,int c){
    		if(!iscut[c])return true;
    		return graph::query(col[a],col[b],col[c]);
    	}
    }
    int main(){
    	n = read(),m = read();
    	for(int u,v,i = 1;i <= m;i++)
    		u = read(),v = read(),task1::addedge(u,v),task1::addedge(v,u),task2::addedge(u,v),task2::addedge(v,u);
    	task1::init();
    	task2::init();
    	int q = read();
    	while(q--){
    		int opt = read(),a,b,c,u,v;
    		if(opt == 1)a = read(),b = read(),u = read(),v = read(),puts(task1::query(a,b,u,v) ? "yes" : "no");
    		else a = read(),b = read(),c = read(),puts(task2::query(a,b,c) ? "yes" : "no");
    	}
    	return 0;
    }
    
  • 相关阅读:
    Duilib 控件类html富文本绘制
    再谈如何使用Taglist?
    如何让vim自动显示函数声明-使用 echofunc.vim插件
    vim 标签页 tabnew 等的操作命令
    php数组操作集锦- 掌握了数组操作, 也就掌握了php
    php字符串操作集锦
    thinkphp疑难解决4
    如何保存gnome的linux的 会话?相当于windows下的休眠?
    开发thinkphp的第一步就是给Application目录(不包括其下的文件)777权限, 关闭selinux
    如何更改gnome-screenshot的默认的保存路径?
  • 原文地址:https://www.cnblogs.com/colazcy/p/13828284.html
Copyright © 2011-2022 走看看