bzoj2238 Mst
https://darkbzoj.tk/problem/2238
树链剖分+线段树
给定一个无向带权图,多次询问,每次指定删除一条边,问删除之后图最小生成树的权值和,询问互相独立
其实还是比较简单的
考虑先把给定图的最小生成树跑出来
如果删除的边不在最小生成树上,那显然没有影响,直接输出权值和
如果在最小生成树上,则这个树被分成了两个部分,应该再加入一条边(显然要保留原有的没被删掉的边,如果更换它们,则会造成权值和增大,当然也有可能不变)
再加入的这一条边,应该两个端点一个分别在这个树被分成的这两部分上。取符合这样要求的边的最小权值
考虑哪些边符合这个要求不太容易,但可以考虑一个边能对哪些边产生作用(也就是哪些边删了,这个边可能会被选入)
想象一下就可以知道,是它两个端点在最小生成树上的简单路径经过的所有边
那么用树剖+线段树维护就行了,每次把路径上的边的值和这个边的值取一个 \(\min\)
所以真的挺简单的
注意是把边的信息下方到了点,所以这两个端点的 lca 的值不能被更改
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n,m;
#define N 100005
#define M 200005
struct graph{
int fir[N],nex[M],to[M],wei[M],tot;
inline void add(int u,int v,int w){
to[++tot]=v;wei[tot]=w;
nex[tot]=fir[u];fir[u]=tot;
}
}G;
struct Edge{
int x,y,wei,id;
}edge[100005];
int which[100005],on_tree[100005];
inline int cmp(Edge a,Edge b){return a.wei<b.wei;}
struct tr{
tr *ls,*rs;
int tag,min;
}dizhi[N*2],*root=&dizhi[0];
int tot;
int size[N],deep[N],fa[N],son[N];
int dfn[N],dfscnt,top[N];
int fa_[N];
int find(int k){return k==fa_[k]?k:fa_[k]=find(fa_[k]);}
void dfs1(int u){
size[u]=1;deep[u]=deep[fa[u]]+1;
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(v==fa[u]) continue;
fa[v]=u;dfs1(v);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int topnow){
top[u]=topnow;dfn[u]=++dfscnt;
if(!son[u]) return;
dfs2(son[u],topnow);
for(reg int v,i=G.fir[u];i;i=G.nex[i]){
v=G.to[i];
if(top[v]) continue;
dfs2(v,v);
}
}
inline void pushdown(tr *tree){
if(tree->tag==1e9) return;
int k=tree->tag;tree->tag=1e9;
tree->ls->tag=std::min(tree->ls->tag,k);tree->rs->tag=std::min(tree->rs->tag,k);
tree->ls->min=std::min(tree->ls->tag,k);tree->rs->min=std::min(tree->rs->tag,k);
}
void build(tr *tree,int l,int r){
if(l==r) return tree->min=tree->tag=1e9,void();
int mid=(l+r)>>1;
tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
build(tree->ls,l,mid);build(tree->rs,mid+1,r);
tree->min=tree->tag=1e9;
}
int get(tr *tree,int l,int r,int pos){
if(l==r) return tree->min;
int mid=(l+r)>>1;
pushdown(tree);
return pos<=mid?get(tree->ls,l,mid,pos):get(tree->rs,mid+1,r,pos);
}
void change(tr *tree,int l,int r,int ql,int qr,int k){
if(ql<=l&&r<=qr){
tree->tag=std::min(tree->tag,k);tree->min=std::min(tree->min,k);
return;
}
int mid=(l+r)>>1;
pushdown(tree);
if(ql<=mid) change(tree->ls,l,mid,ql,qr,k);
if(qr>mid) change(tree->rs,mid+1,r,ql,qr,k);
}
inline void Change(int x,int y,int k){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]) x^=y,y^=x,x^=y;
change(root,1,n,dfn[top[x]],dfn[x],k);
x=fa[top[x]];
}
if(dfn[x]<dfn[y]) x^=y,y^=x,x^=y;
if(dfn[x]>dfn[y]) change(root,1,n,dfn[y]+1,dfn[x],k);
}
int main(){
n=read();m=read();
for(reg int i=1;i<=m;i++) edge[i].x=read(),edge[i].y=read(),edge[i].wei=read(),edge[i].id=i;
std::sort(edge+1,edge+1+m,cmp);
for(reg int i=1;i<=m;i++) which[edge[i].id]=i;
for(reg int i=1;i<=n;i++) fa_[i]=i;
int sum=0,cnt=1;
for(reg int i=1,x,y;cnt<n&&i<=m;i++){
x=find(edge[i].x);y=find(edge[i].y);
if(x==y) continue;
fa_[x]=y;cnt++;on_tree[i]=1;sum+=edge[i].wei;
G.add(edge[i].x,edge[i].y,edge[i].wei);G.add(edge[i].y,edge[i].x,edge[i].wei);
}
if(cnt!=n){
int q=read();while(q--) puts("Not connected");
return 0;
}
// printf("sum : %d\n",sum);
dfs1(1);dfs2(1,1);
build(root,1,n);
for(reg int i=1;i<=m;i++)if(!on_tree[i]) Change(edge[i].x,edge[i].y,edge[i].wei);
int q=read(),t;while(q--){
t=which[read()];
if(!on_tree[t]) printf("%d\n",sum);
else{
int tmp;
if(edge[t].x==fa[edge[t].y]) tmp=edge[t].y;
else tmp=edge[t].x;
tmp=get(root,1,n,dfn[tmp]);
if(tmp==1e9) puts("Not connected");
else printf("%d\n",sum-edge[t].wei+tmp);
}
}
return 0;
}