【题解】CF487E Tourists
作者:互联网
写完这题已经完全自闭了 调了好久……
题目大意
就是求一张图中两点间所有路径中经过的点的最小值,带修。
解法
我们先考虑一下性质:对于无向图显然不好做,考虑一下咋转化成一棵树。
那就往圆方树考虑呗,本题有啥性质?
观察到:
对于一个点双,必然存在一条路径走过该点双中的最小值。
证明比较简单。由于点双内两点之间没有一个必经点,所以我们必定可以留出一个点从走过的那个最小值点再走出去。
那么就考虑建立圆方树叭。码套路,先上一个 tarjan 找点双……
细节1 注意点双的形成是需要满足 low[j]>=dfn[x]
的,不要犯傻打错了。
好的,接下来怎么修改?考虑回答询问的同时其实也就能想到怎么维护修改:扔一个树剖就好了。
那就考虑写树剖叭。码套路,先上两个 dfs ……
细节2 注意树剖的 query
啥的别打错了……
好嘞,上述准备工作都做好了,写个修改测样例……挂掉了。
冷静分析:发现不论改方点还是圆点,都会影响到周围的点。
怎么办,暴力修改?那显然不切实际,一个菊花图就可以卡爆你。
继续考虑如何高效维护。我们知道,一个方点代表一个点双,那么,对于圆方树的树结构,每一个方点的答案其实除了它的孩子还要包含其父亲。这点也就恰好让维护变得不方便起来。
那么我们不妨先考虑无视掉父亲的影响,只维护孩子,怎么做?
维护一个 multiset
对每个方点,每次直接拿这个来暴力更新方点权值。
好的,现在这里可以实现了,那带上父亲呢?
冷静分析一波,这个父亲的影响需要满足两个条件:
-
这个点是方点
-
这个点的父亲没有被计算到
那同时满足下列两个条件的点就只有可能是……
最近公共祖先 \((LCA)\)
所以对于每一组询问实际只有一个点需要被特殊对待!
那么就直接做就好了。
最后再附赠细节3:
更新 low[x]
不要拿 low[j]
啊……(在不 dfs 的时候),调了好久……
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=200100;
int head[N],tot,Head[N],tto,n,m,q,w[N];
struct E {
int nxt,to;
} e[N<<1],edge[N<<1];
inline int read(){
int s=0;
char ch=getchar();
while(!isdigit(ch))ch=getchar();
while(isdigit(ch)){
s=s*10-'0'+ch;
ch=getchar();
}
return s;
}
int nodepos,ddf,rk[N],cir[N],To[N];
inline int Min(int x,int y) {return x<y?x:y;}
multiset<int> S[N];
inline void link(int x,int y,int w=0) {
if(w) {
edge[++tto]=(E) {
Head[x],y
};
Head[x]=tto;
return;
}
e[++tot]=(E) {
head[x],y
};
head[x]=tot;
}
int son[N],siz[N],dfn[N],low[N],st[N],top;
int ttop[N],pa[N],dfstime,vis[N],dep[N];
void dfs1(int x,int fa) {
siz[x]=1;
dep[x]=dep[fa]+1;
pa[x]=fa;
for(int i=Head[x]; i; i=edge[i].nxt) {
int j=edge[i].to;
if(j==fa)continue;
dfs1(j,x);
siz[x]+=siz[j];
if(siz[j]>siz[son[x]])son[x]=j;
}
}
void dfs2(int x,int t) {
rk[dfn[x]=++ddf]=x;
ttop[x]=t;
if(!son[x])return;
dfs2(son[x],t);
for(int i=Head[x]; i; i=edge[i].nxt) {
int j=edge[i].to;
if(j==son[x]||j==pa[x])continue;
dfs2(j,j);
}
}
void tarjan(int x,int root) {
dfn[x]=low[x]=++dfstime;
st[++top]=x;
if(x==root&&!head[x]) {
++nodepos;
To[nodepos]=x;
cir[x]=nodepos;
link(x,nodepos,1);
link(nodepos,x,1);
return;
}
int ch=0;
for(int i=head[x]; i; i=e[i].nxt) {
int j=e[i].to;
if(!dfn[j]) {
++ch;
tarjan(j,root);
low[x]=Min(low[x],low[j]);
if(dfn[x]<=low[j]&&x!=root)vis[x]=1;
if(x==root&&ch>1)vis[x]=1;
if(low[j]>=dfn[x]) {
int vex=-1;
++nodepos;
To[nodepos]=x;
do {
vex=st[top--];
cir[vex]=nodepos;
link(vex,nodepos,1);
link(nodepos,vex,1);
} while(vex!=j);
cir[x]=nodepos;
link(nodepos,x,1);
link(x,nodepos,1);
}
} else low[x]=Min(low[x],dfn[j]);
}
}
namespace SGT {
int mi[N<<2],ls[N<<2],rs[N<<2],node;
inline void pushup(int x) {
mi[x]=Min(mi[ls[x]],mi[rs[x]]);
}
void build(int &x,int l,int r) {
x=++node;
if(l==r) {
mi[x]=w[rk[l]];
return;
}
int mid=(l+r)>>1;
build(ls[x],l,mid);
build(rs[x],mid+1,r);
pushup(x);
}
void change(int x,int L,int R,int pos,int v) {
if(L==R) {
mi[x]=v;
return;
}
int mid=(L+R)>>1;
if(pos<=mid)change(ls[x],L,mid,pos,v);
else change(rs[x],mid+1,R,pos,v);
pushup(x);
}
int query(int x,int L,int R,int l,int r) {
if(L>=l&&R<=r){return mi[x];}
int mid=(L+R)>>1,res=(1LL<<60);
if(l<=mid)res=query(ls[x],L,mid,l,r);
if(mid<r)res=Min(res,query(rs[x],mid+1,R,l,r));
return res;
}
}
using namespace SGT;
int rt;
int Query(int u,int v) {
int res=(1LL<<60);
while(ttop[u]!=ttop[v]) {
if(dep[ttop[u]]<dep[ttop[v]])swap(u,v);
res=Min(res,query(rt,1,nodepos,dfn[ttop[u]],dfn[u]));
u=pa[ttop[u]];
}
if(dep[u]<dep[v])swap(u,v);
res=Min(res,query(rt,1,nodepos,dfn[v],dfn[u]));
if(dep[u]>dep[v])swap(u,v);
if(u>n)res=Min(res,w[pa[u]]);
return res;
}
void Change(int pos,int v) {
change(rt,1,nodepos,dfn[pos],v);
if(pos==1) {
w[pos]=v;
return;
}
int o=pa[pos];
S[o-n].erase(S[o-n].find(w[pos]));
S[o-n].insert(v);
int minv=*S[o-n].begin();
if(minv==w[o]){
w[pos]=v;
return;
}
change(rt,1,nodepos,dfn[o],minv);
w[o]=minv;
w[pos]=v;
}
inline void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
signed main() {
freopen("in.txt","r",stdin);
n=read();m=read();q=read();
for(int i=1; i<=n; ++i)w[i]=read();
for(int i=1; i<=m; ++i) {
int u=read(),v=read();
link(u,v);
link(v,u);
}
nodepos=n;
tarjan(1,1);
memset(dfn,0,sizeof dfn);
dfs1(1,0);
dfs2(1,1);
for(int i=2; i<=n; ++i) {
S[pa[i]-n].insert(w[i]);
}
for(int i=n+1; i<=nodepos; ++i)
w[i]=(S[i-n].empty()?(1LL<<60):(*S[i-n].begin()));
memset(mi,0x7f,sizeof mi);
build(rt,1,nodepos);
while(q--) {
int u,v;
char opt;
cin>>opt;
u=read();v=read();
if(opt=='A') {
write(Query(u,v));
putchar('\n');
continue;
}
Change(u,v);
}
return 0;
}
标签:nodepos,int,题解,pos,++,dfn,low,CF487E,Tourists 来源: https://www.cnblogs.com/h-lka/p/15315107.html