1. 程式人生 > 實用技巧 >[CF1387B1] Village (Minimum) - 貪心,樹形dp

[CF1387B1] Village (Minimum) - 貪心,樹形dp

CF487E Tourists

前排膜拜T神

上面的話和這道題一點關係都沒有

題意

給一個點帶權的無向圖,每次詢問查詢兩點間所有簡單路徑上最小值的最小值,單點修改。

思路

一眼圓方樹。

​ ——@gxy001

這種題只有在樹上做才比較好處理這麼多次詢問。考慮廣義圓方樹,縮完點雙連通分量後建立的方點和圓點。

因為我們找的是最小值,所以必須將代表整個點雙的方點的權值設為與其相連的所有圓點的權值的最小值。用一個 multiset 或者一個可刪堆(或者堆帶懶惰刪除)維護即可。

這樣我們就可以在樹上使用樹剖進行查詢了。

但是這題帶修改。也就是說,按照上面的方法,更改一個圓點就必須遍歷與其相連的所有方點進行修改。這樣會被菊花圖卡死。於是我們用一個經典套路:方點只維護其兒子的權值。這樣我們查詢時方法相同,只不過如果求出的 LCA 為方點的話需要再考慮一下其父親節點的貢獻。

由於我們需要區間查詢加修改,我們可以將圓方樹樹剖並用線段樹維護最小值。

程式碼

如果您追求更快速的程式碼體驗,建議使用手寫堆+惰性刪除。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
inline int read(){
	int w=0,x=0;char c=getchar();
	while(!isdigit(c))w|=c=='-',c=getchar();
	while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return w?-x:x;
}
namespace star
{
	const int maxn=2e5+10,INF=0x3f3f3f3f;
	int n,m,Q,a[maxn],cnt;
	struct gragh{
		int ecnt,head[maxn],to[maxn<<1],nxt[maxn<<1];
		inline void addedge(int a,int b){
			to[++ecnt]=b,nxt[ecnt]=head[a],head[a]=ecnt;
			to[++ecnt]=a,nxt[ecnt]=head[b],head[b]=ecnt;
		}
	}G1,G2;
	int st[maxn],dfn[maxn],tot,id[maxn];
	struct SegmentTree{
		#define ls (ro<<1)
		#define rs (ro<<1|1)
		#define mid ((l+r)>>1)
		int mn[maxn<<2];
		inline void pushup(const int &ro){
			mn[ro]=min(mn[ls],mn[rs]);
		}
		void build(const int& ro=1,const int &l=1,const int &r=cnt){
			if(l==r) return mn[ro]=a[id[l]],void();
			build(ls,l,mid);build(rs,mid+1,r);
			pushup(ro);
		}
		void update(const int &x,const int &ro=1,const int &l=1,const int &r=cnt){
			if(l==r) return mn[ro]=a[id[l]],void();
			if(x<=mid) update(x,ls,l,mid);
			else update(x,rs,mid+1,r);
			pushup(ro);
		}
		int query(const int &x,const int &y,const int &ro=1,const int &l=1,const int &r=cnt){
			if(x==l and y==r) return mn[ro];
			if(y<=mid) return query(x,y,ls,l,mid);
			else if(x>mid) return query(x,y,rs,mid+1,r);
			else return min(query(x,mid,ls,l,mid),query(mid+1,y,rs,mid+1,r));
		}
		#undef ls
		#undef rs
		#undef mid
	}T;
	void tarjan(int x){
		dfn[x]=id[x]=++tot;
		st[++st[0]]=x;
		for(int i=G1.head[x];i;i=G1.nxt[i]){
			int u=G1.to[i];
			if(!dfn[u]){
				tarjan(u);
				id[x]=min(id[x],id[u]);
				if(id[u]>=dfn[x]){
					cnt++;
					int now=-1;
					G2.addedge(x,cnt);
					while(now^u) now=st[st[0]--],G2.addedge(now,cnt);
				}
			}else id[x]=min(id[x],dfn[u]);
		}
	}
	int dep[maxn],siz[maxn],fa[maxn],son[maxn],top[maxn];
	multiset<int> q[maxn];
	void dfs1(int x,int f){
		fa[x]=f,dep[x]=dep[f]+1,siz[x]=1;
		for(int i=G2.head[x];i;i=G2.nxt[i]){
			int u=G2.to[i];
			if(u==f)continue;
			dfs1(u,x);
			if(x>n) q[x-n].insert(a[u]);
			siz[x]+=siz[u];
			if(siz[u]>siz[son[x]])son[x]=u;
		}
		if(x>n) a[x]=*q[x-n].begin();
	}
	void dfs2(int x,int topf){
		top[x]=topf,dfn[x]=++tot,id[tot]=x;
		if(!son[x])return;
		dfs2(son[x],topf);
		for(int i=G2.head[x];i;i=G2.nxt[i]){
			int u=G2.to[i];
			if(u==fa[x] or u==son[x])continue;
			dfs2(u,u);
		}
	}
	inline int LCA(int x,int y){
		int ans=INF;
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]]) swap(x,y);
			ans=min(ans,T.query(dfn[top[x]],dfn[x]));
			x=fa[top[x]];
		}
		if(dep[x]<dep[y])swap(x,y);
		ans=min(ans,T.query(dfn[y],dfn[x]));
		if(y>n) ans=min(ans,a[fa[y]]);
		return ans;
	}
	inline bool gc(){
		char c=getchar();
		while(!isalpha(c))c=getchar();
		return c=='C';
	}
	inline void work(){
		n=cnt=read(),m=read(),Q=read();
		for(int i=1;i<=n;i++) a[i]=read();
		for(int i=1;i<=m;i++) G1.addedge(read(),read());
		tarjan(1);
		tot=0;
		dfs1(1,0);
		dfs2(1,1);
		T.build();
		while(Q--)
			if(gc()){
				int x=read(),w=read(),u=fa[x];
				if(u)
					q[u-n].erase(q[u-n].find(a[x])),
					q[u-n].insert(w),
					a[u]=*q[u-n].begin(),
					T.update(dfn[u]);
				a[x]=w;
				T.update(dfn[x]);
			}else printf("%d\n",LCA(read(),read()));
	}
}
signed main(){
	star::work();
	return 0;
}