zoukankan      html  css  js  c++  java
  • CF487E Tourists

    板子大集合?那我不看题解也没做出来/kk
    思路不算难,但是实现起来比较码农

    tarjan 建广义圆方树 + 对圆方树树剖 + 用 multiset 维护每个双联通分量最小值

    传送门
    给出一个图,每个点有一个权值
    现在给 (q) 个操作

    • 更改一个点的权值
    • 查询 (x)(y) 的所有简单路径(不能有重复的点)中,能走到的最小权值是多少

    首先,要会用 multiset,还要会tarjan,并且会用他构建广义圆方树,这里

    再来看这个题,可以猜出 一个结论:一个点双连通图(分量),对于其中任意三个点 (a,b,c),一定能找出一条 (a,b) 间的简单路径,使得其穿过 (c)
    因为一个点双应该是一个环,然后再加上一堆边组成的,所以找到上面要求的这样一个路径其实很容易,所以就 感性理解 一下就好
    比较详细的证明CF官方题解上有

    那么,我们只要走到一个点双中,就直接取这个点双的最小值就行了,然后可以继续往前走出去
    所以就建一棵圆方树就好了,每个方点维护这个点双中的最小值
    对每个方点,还要用一个 multiset(自带排序)来存这个点双的所有点的权值,方便删除和添加新权值来实现更改

    对于查询,就直接查这两个点在树上的路径中权值最小值就行了,用树剖维护
    考虑怎么修改
    发现如果像上面那样维护,每次修改一个圆点权值,都会波及到他周围所有的方点,容易被菊花图卡飞
    所以,每个方点的权值应该是:他所有相邻的圆点(就是他所对应的那个点双中的所有点),除了它在圆方树上的父亲,中的点权最小值
    这样,修改操作时,每个圆点被修改,都只会波及到它的父亲,就只去更改一下父亲的信息就行了
    查询时也要加一个,就是如果 (x,y) 的 LCA 是个方点,那么还要和它的父亲(是个圆点)的权值取 (min)

    ( exttt{code.}) 写了我一上午/kk

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<map>
    #include<set>
    #include<iomanip>
    #include<cstring>
    #define reg register
    #define EN std::puts("")
    #define LL long long
    inline int read(){
    	register int x=0;register int y=1;
    	register char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    #define N 200006
    #define M 400006
    int n,m;
    int w[N];
    std::multiset<int>set[N];
    struct graph{
    	int fir[N],nex[M],to[M],tot;
    	inline void add(int u,int v){
    		to[++tot]=v;
    		nex[tot]=fir[u];fir[u]=tot;
    	}
    }G,T;
    struct get_bcc{
    	int top,stack[N];
    	int dfn[N],low[N],dfscnt;
    	int bcccnt;
    	void tarjan(int u){
    		dfn[u]=low[u]=++dfscnt;stack[top++]=u;
    		for(reg int v,i=G.fir[u];i;i=G.nex[i]){
    			v=G.to[i];
    			if(!dfn[v]){
    				tarjan(v);low[u]=std::min(low[u],low[v]);
    				if(low[v]>=dfn[u]){
    					bcccnt++;
    					do{
    						T.add(bcccnt,stack[--top]);T.add(stack[top],bcccnt);
    					}while(stack[top]^v);
    					T.add(bcccnt,u);T.add(u,bcccnt);
    				}
    			}
    			else low[u]=std::min(low[u],dfn[v]);
    		}
    	}
    }BCC;
    struct TREE{
    	int fa[N],deep[N],size[N],son[N],index[N];
    	int top[N],dfn[N],dfscnt;
    	void dfs1(int u,int fat){
    		fa[u]=fat;deep[u]=deep[fat]+1;size[u]=1;
    		for(reg int v,i=T.fir[u];i;i=T.nex[i]){
    			v=T.to[i];
    			if(v!=fat){
    				dfs1(v,u);size[u]+=size[v];
    				if(size[v]>size[son[u]]) son[u]=v;
    			}
    		}
    	}
    	void dfs2(int u,int topnow){
    		top[u]=topnow;dfn[u]=++dfscnt;index[dfscnt]=u;
    		if(!son[u]) return;
    		dfs2(son[u],topnow);
    		for(reg int v,i=T.fir[u];i;i=T.nex[i]){
    			v=T.to[i];
    			if(!dfn[v]) dfs2(v,v);
    		}
    	}
    	struct tr {
    		tr *ls,*rs;
    		int min;
    	}dizhi[N<<1],*root=&dizhi[0];
    	int tot;
    	inline void pushup(tr *tree){tree->min=std::min(tree->ls->min,tree->rs->min);}
    	void build(tr *tree,int l,int r){
    		if(l==r) return tree->min=w[index[l]],void();
    		int mid=(l+r)>>1;
    		tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
    		build(tree->ls,l,mid);build(tree->rs,mid+1,r);
    		pushup(tree);
    	}
    	void change(tr *tree,int l,int r,int pos,int k){
    		if(l==r) return tree->min=k,void();
    		int mid=(l+r)>>1;
    		if(pos<=mid) change(tree->ls,l,mid,pos,k);
    		else change(tree->rs,mid+1,r,pos,k);
    		pushup(tree);
    	}
    	int get_min(tr *tree,int l,int r,int ql,int qr){
    		if(ql<=l&&r<=qr) return tree->min;
    		int mid=(l+r)>>1;
    		if(qr<=mid) return get_min(tree->ls,l,mid,ql,qr);
    		if(ql>mid) return get_min(tree->rs,mid+1,r,ql,qr);
    		return std::min(get_min(tree->ls,l,mid,ql,qr),get_min(tree->rs,mid+1,r,ql,qr));
    	}
    	inline int find(int x,int y){
    		int ret=0x7f7f7f7f;
    		while(top[x]!=top[y]){
    			if(deep[top[x]]<deep[top[y]]) x^=y,y^=x,x^=y;
    			ret=std::min(ret,get_min(root,1,BCC.bcccnt,dfn[top[x]],dfn[x]));
    			x=fa[top[x]];
    		}
    		if(deep[x]>deep[y]) x^=y,y^=x,x^=y;
    		ret=std::min(ret,get_min(root,1,BCC.bcccnt,dfn[x],dfn[y]));
    		if(x>n) ret=std::min(ret,w[fa[x]]);
    		return ret;
    	}
    }Tree;
    int main(){
    //		std::freopen("tmp.txt","r",stdin);
    	BCC.bcccnt=n=read();m=read();int q=read();
    	for(reg int i=1;i<=n;i++) w[i]=read();
    	for(reg int u,v,i=1;i<=m;i++){
    		u=read();v=read();
    		G.add(u,v);G.add(v,u);
    	}
    	BCC.tarjan(1);
    //		std::printf("bcccnt : %d
    tot of edges : %d
    
    ",BCC.bcccnt,T.tot);
    	Tree.dfs1(1,0);
    	Tree.dfs2(1,1);
    	for(reg int i=2;i<=n;i++) set[Tree.fa[i]-n].insert(w[i]);
    	//一定从 2 开始循环,因为 1 是跟,用它的父亲(其实他也没有父亲,这里说的是它的 fa 数组),减 n 减成负数,RE 
    	for(reg int i=n+1;i<=BCC.bcccnt;i++)
    		w[i]=set[i-n].empty()?0x7f7f7f7f:*set[i-n].begin();
    	Tree.build(Tree.root,1,BCC.bcccnt);
    	reg int x,y;char op;
    	while(q--){
    		std::scanf("%c",&op);
    		x=read();y=read();
    		if(op=='C'){
    			Tree.change(Tree.root,1,BCC.bcccnt,Tree.dfn[x],y);
    			if(x==1){w[1]=y;continue;}
    			int ind=Tree.fa[x]-n;
    			set[ind].erase(set[ind].find(w[x]));
    			set[ind].insert(y);
    			int min=*set[ind].begin();
    			w[x]=y;
    			if(min!=w[ind+n]) Tree.change(Tree.root,1,BCC.bcccnt,Tree.dfn[ind+n],min);
    			w[ind+n]=min;
    		}
    		else std::printf("%d
    ",Tree.find(x,y));
    	}
    	return 0;
    }
    ```CF487E Tourists
  • 相关阅读:
    显示器的分类和主要性能指标
    关闭Win 10 自动更新功能
    MySQL下载安装教程
    经济学十大原理
    西方经济学概述(经济学原理 1 )
    工作表基本操作
    输入和编辑工作表
    因特网概述
    摩尔定律(Moore's Law)
    C 语言编程机制
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12766910.html
Copyright © 2011-2022 走看看