大意:给定一个图,每次求删去一条边后的 M S T MST MST
先做一次最小生成树
考虑到如果当前删除的不是 M S T MST MST上的边
则对答案没有影响
而如果我们删去边 ( u , v ) (u,v) (u,v)后 M S T MST MST新加进了一条边 ( x , y ) (x,y) (x,y)
则 ( x , y ) (x,y) (x,y)必定和未删去时的 M S T MST MST构成一个环
而且 ( u , v ) (u,v) (u,v)必定在这个环里
换句话说
删去边 ( u , v ) (u,v) (u,v)后只有可能加进满足这样条件的 ( x , y ) (x,y) (x,y),而且肯定是加 v a l val val最小的一条
那我们考虑对每一条不在最小生成树的边维护一下它可能成为删去哪些边的答案
很明显是这条边和当前 M S T MST MST上构成的环
而考虑到每条边都是取最小的
就把边权转成点权
每个非树边给环上的点取个 m i n min min
用线段树维护一下区间最小、区间取 m i n min min就可以了
#include<bits/stdc++.h>
using namespace std;
#define gc getchar
inline int read(){
int res=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while((isdigit(ch)))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
return res;
}
const int N=100005;
int n,m,adj[N],nxt[N<<1],to[N<<1],c[N],ans,q,cnt;
char op[4];
namespace Seg{
#define lc (u<<1)
#define rc ((u<<1)|1)
#define mid ((l+r)>>1)
const int inf=1e9;
int tr[N<<2],tag[N<<2],idx[N<<2];
inline void pushup(int u){
tr[u]=min(tr[lc],tr[rc]);
}
inline void pushdown(int u){
if(tag[u]==inf)return;
tag[lc]=min(tag[lc],tag[u]),tag[rc]=min(tag[rc],tag[u]);
tr[lc]=min(tr[lc],tag[u]),tr[rc]=min(tr[rc],tag[u]),tag[u]=inf;
}
void buildtree(int u,int l,int r){
tag[u]=inf;
if(l==r){
tr[u]=inf;return;
}
buildtree(lc,l,mid);
buildtree(rc,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int st,int des,int k){
if(st<=l&&r<=des){
tr[u]=min(tr[u],k),tag[u]=min(tag[u],k);return;
}
pushdown(u),pushup(u);
if(st<=mid)update(lc,l,mid,st,des,k);
if(mid<des)update(rc,mid+1,r,st,des,k);
}
int query(int u,int l,int r,int st,int des){
if(st<=l&&r<=des){
return tr[u];
}
pushdown(u),pushup(u);
int res=inf;
if(st<=mid)res=min(res,query(lc,l,mid,st,des));
if(mid<des)res=min(res,query(rc,mid+1,r,st,des));
return res;
}
}
using namespace Seg;
namespace SLPF{
int pos[N],top[N],fa[N],siz[N],son[N],dep[N],tot;
inline void addedge(int u,int v){
nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v;
}
void dfs1(int u){
siz[u]=1;
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(v==fa[u])continue;
fa[v]=u,dep[v]=dep[u]+1;
dfs1(v),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp){
pos[u]=++tot,idx[tot]=u,top[u]=tp;
if(!son[u])return;
dfs2(son[u],tp);
for(int e=adj[u];e;e=nxt[e]){
int v=to[e];
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
inline void pathupdate(int u,int v,int w){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
update(1,1,n,pos[top[u]],pos[u],w);
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
update(1,1,n,pos[v]+1,pos[u],w);//注意边转点pos+1
}
inline int pathquery(int u,int v){//其实可以直接写成2个点单点查值,但本人懒癌晚期直接贴之前的板子了
int res=inf;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
res=min(res,query(1,1,n,pos[top[u]],pos[u]));
u=fa[top[u]];
}
if(dep[u]<dep[v])swap(u,v);
res=min(res,query(1,1,n,pos[v]+1,pos[u]));//注意边转点pos+1
return res;
}
}
using namespace SLPF;
namespace Kruscal{
struct edge{
int u,v,w,id,vis;
}e[N];
int fa[N];
inline bool comp(const edge &a,const edge &b){
return a.w<b.w;
}
inline bool cmp(const edge &a,const edge &b){
return a.id<b.id;
}
int find(int x){
return fa[x]==x?fa[x]:fa[x]=find(fa[x]);
}
inline int kruscal(){
sort(e+1,e+m+1,comp);int res=0,num=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int f1=find(e[i].u),f2=find(e[i].v);
if(f1!=f2){
fa[f2]=f1,res+=e[i].w,e[i].vis=1;num++;
addedge(e[i].u,e[i].v),addedge(e[i].v,e[i].u);
}
}
sort(e+1,e+m+1,cmp);
if(num<n-1)return -1;
return res;
}
}
using namespace Kruscal;
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
e[i].u=read(),e[i].v=read(),e[i].w=read(),e[i].id=i;
}
ans=kruscal();
dfs1(1),dfs2(1,1),buildtree(1,1,n);
for(int i=1;i<=m;i++){
if(!e[i].vis)
pathupdate(e[i].u,e[i].v,e[i].w);
}
q=read();
for(int i=1;i<=q;i++){
if(ans==-1){
cout<<"Not connected"<<'\n';continue;
}
int x=read();
if(!e[x].vis){cout<<ans<<'\n';continue;}
int tmp=pathquery(e[x].u,e[x].v);
if(tmp==inf)cout<<"Not connected"<<'\n';
else cout<<(ans+tmp-e[x].w)<<'\n';
}
}