【题目】
题目描述:
给出一个 n n n 个点 m m m 条边的无向带权图,以及 q q q 个询问,每次询问在图中删掉一条边后图的最小生成树。(各询问间独立,每次询问不会对之后的询问产生影响,即被删掉的边在下一条询问中依然存在)
输入格式:
第一行两个正整数 n , m ( n ≤ 50000 , m ≤ 100000 ) n,m(n≤50000,m≤100000) n,m(n≤50000,m≤100000) 表示原图的顶点数和边数。
下面 m m m 行,每行三个整数 x , y , w x,y,w x,y,w 描述了图的一条边 ( x , y ) (x,y) (x,y),其边权为 w ( w ≤ 10000 ) w(w≤10000) w(w≤10000)。保证两点之间至多只有一条边。
接着一行一个正整数 q q q,表示询问数。 ( 1 ≤ q ≤ 100000 ) (1≤q≤100000) (1≤q≤100000)
下面 q q q 行,每行一个询问,询问中包含一个正整数 t t t,表示把编号为 t t t 的边删掉(边从 1 1 1 到 m m m 按输入顺序编号)。
输出格式:
q q q 行,对于每个询问输出对应最小生成树的边权和的值,如果图不连通则输出 “Not connected”
样例数据:
输入
4 4
1 2 3
1 3 5
2 3 9
2 4 1
4
1
2
3
4
输出
15
13
9
Not connected
数据规模:
10
%
10\%
10% 的数据
n
,
m
,
q
≤
100
n,m,q\le 100
n,m,q≤100。
另外
30
%
30\%
30% 的数据,
n
≤
1000
n\le 1000
n≤1000。
100
%
100\%
100% 的数据如题目。
【分析】
一道比较妙的题
首先用 k r u s k a l kruskal kruskal 把整张图的最小生成树求出来
如果要删掉的边是非树边,那答案就是最小生成树的值,现在考虑删掉树边
对于一条树边,考虑哪些非树边可以"代替"它(也就是删掉这条树边,加上非树边后依旧是一颗生成树)
不难发现的是,只有这条树边在非树边两端点的简单路径上,那才可以被替代
所以我们就从能把它替代的所有非树边中找出最短的一条,那此时的最小生成树自然是最小的
如果用 Min[i] 表示能替代 i i i 这条边的最短非树边的话,那么就枚举每条非树边,对于这条非树边两端点的简单路径上的所有树边取个 min(Min[i],w[i]),w[i] 是边权(就是预处理出 Min[i])
查询的时候 ans=MST-w[x]+Min[x],MST 是最小生成树的值
因此用树链剖分维护链修改,用线段树维护区间取 m i n min min
然后,Not connected 有两种情况:
- 在不删边的情况下就已经不连通,全部输出 Not connected
- 没有发现能替代当前树边的非树边,输出 Not connected
注意转边为点
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 50005
#define M 200005
#define inf 0x3f3f3f3f
using namespace std;
int n,m,q,t,tot;
int first[N],v[M],nxt[M];
int father[N],ID[M],Min[N<<2],mark[N<<2];
int fa[N],dep[N],son[N],top[N],pos[N],Size[N];
bool tree[M];
struct edge{int u,v,w,id;}e[M];
bool comp1(const edge &p,const edge &q){return p.w<q.w;}
bool comp2(const edge &p,const edge &q){return p.id<q.id;}
int find(int x)
{
if(father[x]==x) return x;
return father[x]=find(father[x]);
}
void add(int x,int y)
{
nxt[++t]=first[x];
first[x]=t,v[t]=y;
}
int Kruskal()
{
int x,y,i,ans=0,tot=0;
sort(e+1,e+m+1,comp1);
for(i=1;i<=n;++i) father[i]=i;
for(i=1;i<=m;++i)
{
x=find(e[i].u);
y=find(e[i].v);
if(x!=y)
{
father[y]=x;
tot++,ans+=e[i].w;
tree[e[i].id]=true;
add(e[i].u,e[i].v),add(e[i].v,e[i].u);
}
}
sort(e+1,e+m+1,comp2);
return (tot==n-1)?ans:-1;
}
void dfs1(int x)
{
int i,k;
Size[x]=1;
for(i=first[x];i;i=nxt[i])
{
if((k=v[i])==fa[x]) continue;
fa[k]=x,dep[k]=dep[x]+1,dfs1(k),Size[x]+=Size[k];
if(Size[son[x]]<Size[k]) son[x]=k;
}
}
void dfs2(int x,int tp)
{
int i;
top[x]=tp,pos[x]=++tot;
if(son[x]) dfs2(son[x],top[x]);
for(i=first[x];i;i=nxt[i])
if(v[i]!=son[x]&&v[i]!=fa[x])
dfs2(v[i],v[i]);
}
void pushnow(int root,int z)
{
Min[root]=min(Min[root],z);
mark[root]=min(mark[root],z);
}
void pushdown(int root)
{
if(mark[root]==inf) return;
pushnow(root<<1,mark[root]),pushnow(root<<1|1,mark[root]);
mark[root]=0x3f3f3f3f;
}
void modify(int root,int l,int r,int x,int y,int z)
{
if(l>=x&&r<=y)
{
pushnow(root,z);
return;
}
pushdown(root);
int mid=(l+r)>>1;
if(x<=mid) modify(root<<1,l,mid,x,y,z);
if(y>mid) modify(root<<1|1,mid+1,r,x,y,z);
Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void change(int x,int y,int z)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
modify(1,1,n,pos[top[x]],pos[x],z),x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
modify(1,1,n,pos[x]+1,pos[y],z);
}
int query(int root,int l,int r,int x)
{
if(l==r) return Min[root];
pushdown(root);int ans=inf,mid=(l+r)>>1;
if(x<=mid) ans=min(ans,query(root<<1,l,mid,x));
else ans=min(ans,query(root<<1|1,mid+1,r,x));
return ans;
}
int main()
{
int x,y,z,i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;++i)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w),e[i].id=i;
scanf("%d",&q);
int MST=Kruskal();
dfs1(1),dfs2(1,1);
memset(Min,0x3f,sizeof(Min));
memset(mark,0x3f,sizeof(mark));
for(i=1;i<=m;++i)
if(!tree[i])
change(e[i].u,e[i].v,e[i].w);
for(i=1;i<=q;++i)
{
scanf("%d",&x);
if(MST==-1) puts("Not connected");
else if(!tree[x]) printf("%d\n",MST);
else
{
int ans=query(1,1,n,max(pos[e[x].u],pos[e[x].v]));
if(ans==inf) puts("Not connected");
else printf("%d\n",MST-e[x].w+ans);
}
}
return 0;
}