好题集 (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;
}

浙公网安备 33010602011771号