zoukankan      html  css  js  c++  java
  • 圆方树

    圆方树

    重要的:::注意点权赋值

    在一般无向图上使用它

    •配合点双连通分量缩点用的

    •对于一个dcc,建立一个方点,dcc内所有点向他连边

    •割点关联多个dcc,连接多个方点

    •这样会形成一棵树,实际上dcc缩点可以有不同的建树方式

    圆方树的点数小于 2n,这是因为割点的数量小于 n,所以请注意各种数组大小要开两倍

    图一:原图,图二:标出点双,图三:圆方树

    image-20200827214846808

    img

    建树代码

    void tarjan(int x) {
    	vis[x]=1;
    	cnt_size++;//非联通图处理每个连通块
    	st[++top]=x;
    	dfn[x]=low[x]=++dfn_cnt;
    	for(int i=hd[x];i;i=nxt[i]) {
    		int y=to[i];
    		if(!dfn[y]) {
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    			if(low[y]==dfn[x]) {//找到一个以x为根的点双连通分量
    				w[++num]=0;//num为方点的编号,初始值为n
    				while(st[top+1]!=y) {//将点双中除了x的点退栈,并在圆方树中连边
    					G[num].push_back(st[top]);
    					G[st[top]].push_back(num);
    					vis[st[top--]]=0;
    					++w[num];
    				}//注意x自身也要连边(但不退栈)
    				G[num].push_back(x);
    				G[x].push_back(num);
    				++w[num];
    			}
    		} else if(vis[y])low[x]=min(low[x],dfn[y]);
    	}
    }
    

    https://www.luogu.com.cn/problem/CF1062F

    没懂

    显然,一个点是重要的点或者次重要的点,那么和它在同一个 scc 内的所有点一定也是重要 的或者次重要的,那么可以仅考虑这个 scc 整体 既然我只需要考虑可达性,那么可以缩点 考虑这个 dag 的拓扑序,重要的点前面的点一定能到达它,它一定能到达后面的点 考虑拓扑排序的过程,当重要的点出队时,由于它能到达后面的所有点,这些点必须由它这 个“祖先”(当然这用在 dag 上不太合适……)扩展出来,所以这时队列一定是空的 这样我们对正图和反图分别拓扑排序(必须保证前后都能成为“祖先”)就能找到重要的点 对于次重要的点,显然被删的那点不可能属于一个大的 scc,一定是一个孤立的点 也就是说,对于一个次重要点只有这么一个孤立点,他俩不能互相到达 这样的话,拓扑排序让这点出队时,队里最多只能剩下这么一个孤立点,这可以画几个例子 理解一下,这样就可以找了 对于 dag,或者缩点之后的 dag,我一定要考虑拓扑排序,你 dp 不也得这么干啊

    铁人两项

    题解见参考blog1

    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=200005; 
    const int M=400005; 
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,num;
    int hd[N],nxt[M],to[M],tot;
    inline void add(int x,int y) {
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    }
    int dfn[N],low[N],dfn_cnt;
    int st[N],top,cnt_size;//连通块内点数
    int w[N];
    bool vis[N];
    vector<int>G[N];
    void tarjan(int x) {
    	vis[x]=1;
    	cnt_size++;
    	st[++top]=x;
    	dfn[x]=low[x]=++dfn_cnt;
    	for(int i=hd[x];i;i=nxt[i]) {
    		int y=to[i];
    		if(!dfn[y]) {
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    			if(low[y]==dfn[x]) {
    				w[++num]=0;
    				while(st[top+1]!=y) {
    					G[num].push_back(st[top]);
    					G[st[top]].push_back(num);
    					vis[st[top--]]=0;
    					++w[num];
    					// top--;
    				}
    				G[num].push_back(x);
    				G[x].push_back(num);
    				++w[num];
    			}
    		} else if(vis[y])low[x]=min(low[x],dfn[y]);
    	}
    }
    long long ans;
    
    int siz[N];
    void dfs(int x,int fa) {
    	siz[x]=(x<=n);
    	for(int i=0;i<G[x].size();i++) {
    		int y=G[x][i];
    		if(y==fa) continue;
    		dfs(y,x);
    		ans+=2ll*w[x]*siz[x]*siz[y];
    		siz[x]+=siz[y];
    	}
    	ans+=2ll*w[x]*siz[x]*(cnt_size-siz[x]);
    }
    int main() {
    	n=read();m=read();
    	for(int i=1;i<=n;i++) w[i]=-1;
    	num=n;//编号
    	for(int i=1,x,y;i<=m;i++) {
    		x=read();y=read();
    		add(x,y),add(y,x);
    	}
    	for(int i=1;i<=n;i++) 
    		if(!dfn[i]) {
    			cnt_size=0;
    			tarjan(i);
    			--top;
    			dfs(i,0);
    		}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    [CodeForces 487E]Tourists

    见参考blog1

    建出原图的圆方树,令方点权值为相邻圆点权值的最小值,问题转化为求路径上最小值

    一次修改一个圆点的点权,需要修改所有和它相邻的方点,这样很容易被卡到 O(n) 个修改。

    但是注意到这样的作为方点父亲的圆点只会在当方点是 s 和 t 的 lca 时才会影响答案,令方点权值为自己的儿子圆点的权值最小值,这样的话修改时只需要修改父亲方点,而在查询时特判一下 lca 方点的父亲即可 这种“考虑何时会对答案产生影响”的思路是相当重要的

    对于方点的维护,只需要对每个方点开一个 multiset 维护权值集合即可。

    #include <set>
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    const int N=200005; 
    const int M=400005; 
    inline int read(){
    	int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    	return f*x;
    }
    int n,m,q,num;
    int hd[N],nxt[M],to[M],tot;
    inline void add(int x,int y) {
    	to[++tot]=y;nxt[tot]=hd[x];hd[x]=tot;
    }
    int dfn[N],low[N],dfn_cnt;
    int st[N],tp;
    
    bool vis[N];
    vector<int>G[N];
    void tarjan(int x) {
    	vis[x]=1;
    	st[++tp]=x;
    	dfn[x]=low[x]=++dfn_cnt;
    	for(int i=hd[x];i;i=nxt[i]) {
    		int y=to[i];
    		if(!dfn[y]) {
    			tarjan(y);
    			low[x]=min(low[x],low[y]);
    			if(low[y]==dfn[x]) {
    				++num;
    				while(st[tp+1]!=y) {
    					G[num].push_back(st[tp]);
    					G[st[tp]].push_back(num);
    					vis[st[tp--]]=0;
    				}
    				G[num].push_back(x);
    				G[x].push_back(num);
    			}
    		} else if(vis[y])low[x]=min(low[x],dfn[y]);
    	}
    }
    multiset<int>s[N];
    int siz[N],fa[N],dep[N],son[N];
    void dfs_son(int x,int f) {
    	fa[x]=f;siz[x]=1;dep[x]=dep[f]+1;
    	for(int i=0;i<G[x].size();i++) {
    		int y=G[x][i];
    		if(y==f) continue;
    		dfs_son(y,x);
    		siz[x]+=siz[y];
    		if(siz[y]>siz[son[x]]) son[x]=y;
    	}
    }
    int rev[N],top[N];
    void dfs_chain(int x,int tp) {
    	top[x]=tp;
    	dfn[x]=++dfn_cnt;rev[dfn_cnt]=x;
    	if(son[x]) dfs_chain(son[x],tp);
    	for(int i=0;i<G[x].size();i++) {
    		int y=G[x][i];
    		if(y==fa[x]||y==son[x]) continue;
    		dfs_chain(y,y);
    	}
    }
    
    #define ls (p<<1)
    #define rs (p<<1|1)
    #define mid ((l+r)>>1)
    int val[N<<2];
    int w[N];
    void build(int l,int r,int p) {
    	if(l==r) {val[p]=w[rev[l]];return;}
    	build(l,mid,ls);
    	build(mid+1,r,rs);
    	val[p]=min(val[ls],val[rs]);
    }
    void modify(int l,int r,int t,int v,int p) {
    	if(l==r) {val[p]=v;return;}
    	if(t<=mid) modify(l,mid,t,v,ls);
    	else modify(mid+1,r,t,v,rs);
    	val[p]=min(val[ls],val[rs]);
    }
    int query(int l,int r,int L,int R,int p) {
    	if(L<=l&&r<=R) return val[p];
    	int ans=0x3f3f3f3f;
    	if(L<=mid) ans=min(ans,query(l,mid,L,R,ls));
    	if(R>mid) ans=min(ans,query(mid+1,r,L,R,rs));
    	return ans;
    }
    int get_min(int x,int y) {
    	int ans=0x3f3f3f3f;
    	while(top[x]!=top[y]) {
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
     		ans=min(ans,query(1,num,dfn[top[x]],dfn[x],1));
    		x=fa[top[x]];
    	}
    	if(dfn[x]>dfn[y]) swap(x,y);
    	ans=min(ans,query(1,num,dfn[x],dfn[y],1));
    	if(x>n) ans=min(ans,w[fa[x]]);//Q
    	return ans;
    }
    int main() {
    	n=read();m=read();q=read();
    	for(int i=1;i<=n;i++) w[i]=read();
    	num=n;//编号
    	for(int i=1,x,y;i<=m;i++) {
    		x=read();y=read();
    		add(x,y),add(y,x);
    	}
    	tarjan(1);
    	dfn_cnt=0;
    	dfs_son(1,0);
    	dfs_chain(1,1);
    	for(int i=1;i<=n;i++)
    		if(fa[i]) s[fa[i]].insert(w[i]);
    	for(int i=n+1;i<=num;i++)
    		w[i]=*s[i].begin();
    	memset(val,0x3f,sizeof(val));
    	build(1,num,1);
    	char op;int x,y;
    	while(q--) {
    		cin>>op;x=read();y=read();
    		if(op=='C') {
    			modify(1,num,dfn[x],y,1);
    			if(fa[x]) {
    				s[fa[x]].erase(s[fa[x]].lower_bound(w[x]));
    				s[fa[x]].insert(y);
    				if(w[fa[x]]!=*s[fa[x]].begin()) {
    					w[fa[x]]=*s[fa[x]].begin();
    					modify(1,num,dfn[fa[x]],w[fa[x]],1);
    				}
    			}
    			w[x]=y;	
    		}
    		else printf("%d
    ",get_min(x,y));
    	}
    	return 0;
    }
    

    wait

    https://loj.ac/problem/2562

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    
    const int MN = 100005;
    
    int N, M, Q, cnt;
    std::vector<int> G[MN], T[MN * 2];
    
    int dfn[MN * 2], low[MN], dfc;
    int stk[MN], tp;
    void Tarjan(int u) {
    	low[u] = dfn[u] = ++dfc;
    	stk[++tp] = u;
    	for (auto v : G[u]) {
    		if (!dfn[v]) {
    			Tarjan(v);
    			low[u] = std::min(low[u], low[v]);
    			if (low[v] == dfn[u]) {
    				++cnt;
    				for (int x = 0; x != v; --tp) {
    					x = stk[tp];
    					T[cnt].push_back(x);
    					T[x].push_back(cnt);
    				}
    				T[cnt].push_back(u);
    				T[u].push_back(cnt);
    			}
    		}
    		else low[u] = std::min(low[u], dfn[v]);
    	}
    }
    
    int dep[MN * 2], faz[MN * 2][18], dis[MN * 2];
    void DFS(int u, int fz) {
    	dfn[u] = ++dfc;
    	dep[u] = dep[faz[u][0] = fz] + 1;
    	dis[u] = dis[fz] + (u <= N);
    	for (int j = 0; j < 17; ++j)
    		faz[u][j + 1] = faz[faz[u][j]][j];
    	for (auto v : T[u]) if (v != fz) DFS(v, u);
    }
    int LCA(int x, int y) {
    	if (dep[x] < dep[y]) std::swap(x, y);
    	for (int j = 0, d = dep[x] - dep[y]; d; ++j, d >>= 1)
    		if (d & 1) x = faz[x][j];
    	if (x == y) return x;
    	for (int j = 17; ~j; --j)
    		if (faz[x][j] != faz[y][j])
    			x = faz[x][j], y = faz[y][j];
    	return faz[x][0];
    }
    
    int main() {
    	int Ti; scanf("%d", &Ti);
    	while (Ti--) {
    		scanf("%d%d", &N, &M);
    		for (int i = 1; i <= N; ++i) {
    			G[i].clear();
    			dfn[i] = low[i] = 0;
    		}
    		for (int i = 1; i <= N * 2; ++i) T[i].clear();
    		for (int i = 1, x, y; i <= M; ++i) {
    			scanf("%d%d", &x, &y);
    			G[x].push_back(y);
    			G[y].push_back(x);
    		}
    		cnt = N;
    		dfc = 0, Tarjan(1), --tp;
    		dfc = 0, DFS(1, 0);
    		scanf("%d", &Q);
    		while (Q--) {
    			static int S, A[MN];
    			scanf("%d", &S);
    			int Ans = -2 * S;
    			for (int i = 1; i <= S; ++i) scanf("%d", &A[i]);
    			std::sort(A + 1, A + S + 1, [](int i, int j) { return dfn[i] < dfn[j]; });
    			for (int i = 1; i <= S; ++i) {
    				int u = A[i], v = A[i % S + 1];
    				Ans += dis[u] + dis[v] - 2 * dis[LCA(u, v)];
    			}
    			if (LCA(A[1], A[S]) <= N) Ans += 2;
    			printf("%d
    ", Ans / 2);
    		}
    	}
    	return 0;
    }
    

    参考blog

    https://www.cnblogs.com/PinkRabbit/p/10446473.html

    https://www.cnblogs.com/cjyyb/p/9098400.html ————仙人掌+圆方树

  • 相关阅读:
    Promise前期准备---区别实例对象与函数对象
    es6之函数参数默认值、字符串方法、for of
    es6之剩余和扩展参数
    es6之解构赋值
    es6之set和map
    前端知识点总结
    jQuery的12种选择器
    前端面试总结
    Closure
    PHP 中 16 个魔术方法详解
  • 原文地址:https://www.cnblogs.com/ke-xin/p/13576056.html
Copyright © 2011-2022 走看看