=== ===
这里放传送门
=== ===
题解
这题的意思是给一个无向图,求从某个点到某个点中间不经过重复点能够到达的所有点的点权最小值,还要支持修改某个点的点权。
因为不能重复到达点,所以当它在某个点的时候,和这个点属于同一个点双联通分量的点肯定全都能用来统计答案。而因为点双缩点以后是一棵树,所以如果能把每个点双缩点,点权设置为它里面所有点的最小值,那么直接跑链剖就可以了
然而把点双摘出来然后编号然后统计点权就是一个非常麻烦的工作。。因为割点同时属于多个点双联通分量,实现起来会有很多细节。。。
比较好写的做法是对每个点双新建一个点,然后每个点向它所属的点双连一条边。这样的话所有的割点会连到好几个点双上去,而非割点只会被连到一个点双上去。这样的话样例里面那个图搞出来就是这个样子:
可以看出这样建树以后树的模式一定是一个点连着一个块再连着一个点这样的。所以每个点的父亲一定是一个代表点双的块,并且这个点一定属于这个点双。那么用一个multiset来维护点双里面的最小值,把所有的点更新到它父亲节点上就可以了。
查询的时候可以直接链剖,但有一个问题就是如果两个点的LCA是一个块,那么这个块的father应该也是一个属于这个块的割点,要把这个点特判一下。
修改的时候除了修改当前点以外还要在multiset里维护一下。
代码
#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,q,val[100010],cnt,tot,p[100010],a[200010],nxt[200010],w[200010],low[200010];
int top,st[200010],rec,point,father[200010];
bool mak[200010];
multiset<int> s[100010];
void add(int x,int y){
tot++;a[tot]=y;nxt[tot]=p[x];p[x]=tot;
}
int rev(int i){
return (i&1)?(i+1):(i-1);}
int find(int x){
if (father[x]==x) return father[x];
father[x]=find(father[x]);
return father[x];
}
void merge(int x,<