好题集 (4) - CF 487E Tourists

题目传送门

题意:给定一简单无向联通图和每个点的点权,支持两种操作:修改某个点的点权,询问两点间所有简单路径上的点权最小值。

看到无向连通图和路径操作,首先联想到一个最容易的做法:建出圆方树,其中方点点权定义为与它相邻的圆点的点权最小值;然后树剖套线段树维护圆点,一个圆点的点权被修改时暴力更新与它相邻的方点。

但是这样做有一个问题:可以构造数据使得某个点同时处于很多(\(O(n)\) 级别)个环中,此时暴力更新方点显然不可做。

考虑新的做法。我们把无向图变成了树,因此不妨从树的一些特殊性质来考虑。

联想到一个重要性质:任何非根结点有且仅有一个父结点

于是,在原思路基础上,我们重新定义一个方点的点权为它所有儿子结点(由圆方树定义,这些结点一定都是圆点)中,点权的最小值。这样一个圆点的点权就仅能影响到一个方点,修改时一个圆点时也就只需暴力修改一个方点。

接下来考虑如何完成这个暴力修改。更具体地,如何扣除之前点权的贡献,然后加入新的点权的贡献。

最小值这一信息不可差分,因此我们考虑用笨一点的方式。对于每个方点,我们都将它所有儿子的点权维护到一个动态的可重集里;每次暴力修改时,都将原来的点权从集合中删除,插入新的点权,再取出集合中最小的数,将这个数赋给方点点权。这个可重集可以直接用 multiset 来实现。

最后还有一个 corner case:若查询时两端点的 LCA 是方点,那么我们的树剖会跳过 LCA 的父亲,我们要查的链上就少了一个点。此时需要特判一下,手动将 LCA 的父亲计入贡献。

于是就做到了 \(O(n\log n)\) 预处理(考虑到 multiset 的插入复杂度是 \(\log\)),\(O(\log^2n)\) 查询,\(O(\log n)\) 修改。这道 *3200 就被解决了。

代码:

#include<iostream>
#include<set>
#include<stack>
using namespace std;

const int N=2e5+5;

int n,m,q;

int w[N]/*点权*/,nw[N]/*树剖后的新点权*/;

namespace OIfast{
	
	char buf[1<<21],*p1,*p2,*top,buffer[1<<21];
	#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?0:*p1++)
	#define gc getchar()
	
	inline int read(){
		int n=0;char c=gc;
		while(!isdigit(c))c=gc;
		while(isdigit(c))n=(n<<3)+(n<<1)+(c^48),c=gc;
		return n;
	}
	
}using namespace OIfast;


namespace SGT{
	
	#define ls u<<1
	#define rs u<<1|1
	#define mid (L+R>>1)
	
	int mn[N<<2];
	
	inline void pushup(int u){
		return mn[u]=min(mn[ls],mn[rs]),void();
	}
	
	inline void build(int u,int L,int R){
		if(L==R)return mn[u]=nw[L],void();
		return build(ls,L,mid),build(rs,mid+1,R),pushup(u),void();
	}
	
	inline void upd(int u,int tar,int val,int L,int R){
		if(tar>R||tar<L)return ;
		if(L==R)return mn[u]=val,void();
		return ((tar<=mid)?(upd(ls,tar,val,L,mid)):(upd(rs,tar,val,mid+1,R))),pushup(u),void();
	}
	
	inline int qry(int u,int l,int r,int L,int R){
		if(l>r)swap(l,r);
		if(l>R||L>r)return 1e9;
		if(l<=L&&R<=r)return mn[u];
		return min(qry(ls,l,r,L,mid),qry(rs,l,r,mid+1,R));
	}
	
	inline int qry_(int u,int tar,int L,int R){
		if(tar>R||tar<L)return 0;
		if(L==R)return mn[u];
		return ((tar<=mid)?(qry_(ls,tar,L,mid)):(qry_(rs,tar,mid+1,R)));
	}
	
	#undef ls
	#undef rs
	#undef mid
	
}using namespace SGT;

namespace graph{
	
	namespace basic{
		
		#define rep1 for(int i=head1[u];~i;i=e1[i].nxt)
		#define rep2 for(int i=head2[u];~i;i=e2[i].nxt)
		#define handle1 int v=e1[i].v;
		#define handle2 int v=e2[i].v;
		
		int idx1=-1,idx2=-1;
		
		int head1[N],head2[N];
		
		struct edge{
			int v,nxt;
		}e1[N<<3],e2[N<<3];
		
		inline void add1(int u,int v){
			return e1[++idx1]={v,head1[u]},head1[u]=idx1,void();
		}
		
		inline void add2(int u,int v){
			return e2[++idx2]={v,head2[u]},head2[u]=idx2,void();
		}
		
	}using namespace basic;
	
	namespace YFS{
		
		int tim/*时间戳*/,id/*方点编号*/;
		
		int low[N],dfn[N];
		stack<int>s;
		
		inline void getmn(int &a,int b){
			return a=(a<b?a:b),void();
		}
		
		inline void Tarjan(int u){
			low[u]=dfn[u]=++tim,s.push(u);
			rep1{
				handle1;
				if(!dfn[v]){
					Tarjan(v);
					getmn(low[u],low[v]);
					if(low[v]^dfn[u])continue ;
					++id;
					while(s.top()^v)add2(id,s.top()),add2(s.top(),id),s.pop();
					add2(u,id),add2(id,u),add2(v,id),add2(id,v),s.pop();
				}else getmn(low[u],dfn[v]);
			}
			return ;
		}
		
	}using namespace YFS;
	
	namespace SP_{
		
		int fa[N],dep[N],sz[N],son[N];
		
		inline void dfs(int u,int f){
			fa[u]=f,sz[u]=1,dep[u]=dep[f]+1;
			rep2{
				handle2;if(v==f)continue ;
				dfs(v,u);
				sz[u]+=sz[v];
				if(sz[son[u]]<sz[v])son[u]=v;
			}
			return ;
		}
		
		int ntim/*时间戳*/;
		
		int top[N],ndfn[N]/*适用于树剖的 dfs 序*/;
		
		inline void df5(int u,int t){
			top[u]=t,ndfn[u]=++ntim,nw[ntim]=w[u];
			if(!son[u])return ;
			df5(son[u],t);
			rep2{
				handle2;if(v==fa[u]||v==son[u])continue ;
				df5(v,v);
			}
			return ;
		}
		
		inline int LCA(int u,int v){
			while(top[u]^top[v]){
				if(dep[top[u]]<dep[top[v]])swap(u,v);
				u=fa[top[u]];
			}
			return dep[u]<dep[v]?u:v;
		}
		
		multiset<int>val[N]/*方点的权值集合*/;
		
		inline void init(int u){
			w[u]=1e9;
			rep2{
				handle2;if(v>n||v==fa[u])continue ;
				getmn(w[u],w[v]),val[u].insert(w[v]);
			}
			return ;
		}
		
		inline void upd_dot(int u,int val){
			return upd(1,ndfn[u],val,1,id),void();
		}
		
		inline int qry_path(int u,int v){
			int res=1e9;
			while(top[u]^top[v]){
				if(dep[top[u]]<dep[top[v]])swap(u,v);
				getmn(res,qry(1,ndfn[u],ndfn[top[u]],1,id));
				u=fa[top[u]];
			}
			return getmn(res,qry(1,ndfn[u],ndfn[v],1,id)),res;
		}
		
		inline int qry_dot(int u){
			return qry_(1,ndfn[u],1,id);
		}
		
	}using namespace SP_;
	
	#undef rep1
	#undef rep2
	#undef handle1
	#undef handle2
	
}using namespace graph;

inline void work(){
	char op=gc;while(!isalpha(op))op=gc;
	if(1==2){
		puts("wow");
	}else if(op=='C'){
		int a=read(),v=read();
		int f=fa[a];
		if(f){
			val[f].erase(val[f].find(qry_dot(a)));
			val[f].insert(v);
			upd_dot(f,*val[f].begin());
		}
		upd_dot(a,v);
	}else if(op=='A'){
		int a=read(),b=read();
		int lca=LCA(a,b);
		int res=1e9;
		if(lca>n)getmn(res,qry_dot(fa[lca]));
		getmn(res,qry_path(a,b)),printf("%d\n",res);
	}
	return ;
}

signed main(){
	for(int i=0;i<N;++i)head1[i]=head2[i]=-1;
	n=read(),m=read(),q=read();id=n;
	for(int i=1;i<=n;++i)w[i]=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read();
		add1(u,v),add1(v,u);
	}
	Tarjan(1),dfs(1,0);
	for(int u=n+1;u<=id;++u)init(u);
	df5(1,1),build(1,1,id);
	while(q--)work();
	return 0;
}

提交记录 1(无快读)& 提交记录 2(有快读)

posted @ 2025-11-14 19:30  DX3906_ourstar  阅读(10)  评论(0)    收藏  举报