BZOJ2238 Mst

Time Limit: 20 Sec Memory Limit: 256 MB

Description

给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树。(各询问间独立,每次询问不对之后的询问产生影响,即被删掉的边在下一条询问中依然存在)

Input

第一行两个正整数\(N,M(N\leq 50000,M\leq 100000)\)表示原图的顶点数和边数。

下面M行,每行三个整数\(X,Y,W\)描述了图的一条边\((X,Y)\),其边权为\(W(W\leq10000)\)。保证两点之间至多只有一条边。

接着一行一个正整数\(Q\),表示询问数。\((1\leq Q\leq100000)\)

下面\(Q\)行,每行一个询问,询问中包含一个正整数\(T\),表示把编号为\(T\)的边删掉(边从\(1\)\(M\)按输入顺序编号)。

Output

\(Q\)行,对于每个询问输出对应最小生成树的边权和的值,如果图不连通则输出“Not connected”

Sample Input

4 4
1 2 3 
1 3 5 
2 3 9 
2 4 1 
4 
1 
2 
3 
4

Sample Output

15 
13 
9 
Not connected
样例解释: 无

数据规模:

10%的数据N,M,Q<=100。
另外30%的数据,N<=1000
100%的数据如题目。

HINT

更新数据---2015.6.9

Solution

如果这条边不在原来的最小生成树里面的话,我们直接输出原来最小生成树即可。如果在的话,那么我们想想Kruskal的过程,删掉这条边以后,我们只会加进来一条能使得由于没有这条边而导致本应联通却未联通的联通块联通的边权最小的边。

我们画一下最小生成树模拟一下,可以发现,一条不在最小生成树上的边\((x,y)\),可以使得树上从\(x\)\(y\)路径上的一条边边断掉后加上它能联通。

(今天分析这道题的时候就想到了这边就没辙了,后来学长来点拨一下就会了,其实就只差五分之一了)

那么我们就做一个树剖,一开始线段树上全是\(INF\)。然后对于每条不在mst上的边\((x,y)\),对\((x,y)\)的路径做一个和这条边的边权取一个\(min\)。这样就可以求出使得每一条断开后能够重新连接两个联通块的边权的最小值了。于是这道题就做完了。\(m\log^2n\)预处理,\(\log n\)查询,一共\(O(m\log^2n+q\log n)\)

这里有个坑点,如果图本来就不联通那就所有询问都是Not connected

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
#define lowbit(x) ((x)&(-(x)))
#define REP(i,a,n) for(register int i=(a);i<=(n);++i)
#define PER(i,a,n) for(register int i=(a);i>=(n);--i)
#define FEC(i,x) for(register int i=head[x];i;i=g[i].ne)
#define dbg(...) fprintf(stderr,__VA_ARGS__)
#define lc o<<1
#define rc o<<1|1
namespace io{
	const int SIZE=(1<<21)+1;char ibuf[SIZE],*iS,*iT,obuf[SIZE],*oS=obuf,*oT=oS+SIZE-1,c,qu[55];int f,qr;
	#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),(iS==iT?EOF:*iS++)):*iS++)
	inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
	inline void putc(char x){*oS++=x;if(oS==oT)flush();}
	template<class I>inline void read(I &x){for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;}
	template<class I>inline void write(I x){if(!x)putc('0');if(x<0)putc('-'),x=-x;while(x)qu[++qr]=x%10+'0',x/=10;while(qr)putc(qu[qr--]);}
	inline void print(const char *s){while(*s!='\0')putc(*s++);}
	struct Flusher_{~Flusher_(){flush();}}io_flusher_;
}//orz laofudasuan
using io::read;using io::putc;using io::write;using io::print;
typedef long long ll;typedef unsigned long long ull;
template<typename A,typename B>inline bool SMAX(A&x,const B&y){return x<y?x=y,1:0;}
template<typename A,typename B>inline bool SMIN(A&x,const B&y){return y<x?x=y,1:0;}

const int N=50000+7,M=100000+7,INF=0x3f3f3f3f;
int n,m,Q,x,y,intr[M],notcon,sum,hkk;
int dep[N],num[N],son[N],f[N],dfn[N],pre[N],top[N],dfc;
struct Graph{int x,y,z,id;bool operator<(const Graph&a)const{return z<a.z;}}G[M],G2[M];
struct Edge{int to,ne;}g[N<<1];int head[N],tot;
inline void Addedge(int x,int y){g[++tot].to=y,g[tot].ne=head[x],head[x]=tot;}

int fa[N];
inline int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);}
inline void Union(int x,int y){x=Find(x),y=Find(y);fa[y]=x;}
inline void Kruskal(){
	sort(G+1,G+m+1);for(register int i=1;i<=n;++i)fa[i]=i;
	for(register int i=1,cnt=0;i<=m;++i){
		int x=Find(G[i].x),y=Find(G[i].y),z=G[i].z;if(x==y)continue;intr[G[i].id]=1;
		Addedge(G[i].x,G[i].y),Addedge(G[i].y,G[i].x);Union(x,y);++cnt;sum+=z;if(cnt==n-1)return;//错误笔记:漏打了Union这句话!!!! 
	}
	notcon=1;
}

inline void DFS1(int x,int fa=0){
	dep[x]=dep[fa]+1;f[x]=fa;num[x]=1;
	for(register int i=head[x];i;i=g[i].ne){
		int y=g[i].to;if(y==fa)continue;
		DFS1(y,x);num[x]+=num[y];if(num[y]>num[son[x]])son[x]=y;
	}
}
inline void DFS2(int x,int pa){
	top[x]=pa;dfn[x]=++dfc;pre[dfc]=x;
	if(!son[x])return;DFS2(son[x],pa);
	for(register int i=head[x];i;i=g[i].ne){
		int y=g[i].to;if(y==f[x]||y==son[x])continue;
		DFS2(y,y);
	}
}
struct Node{int set;inline Node(){set=INF;}}t[N<<2];
inline void GMIN(int o,int L,int R,int l,int r,int k){
	if(l<=L&&R<=r)return (void)(SMIN(t[o].set,k));
	int M=(L+R)>>1;if(l<=M)GMIN(lc,L,M,l,r,k);if(r>M)GMIN(rc,M+1,R,l,r,k);
}
inline int Qmin(int o,int L,int R,int x){//错误笔记:注意这里应该取得是t[o].set不是val 
	int ans=INF;
	while(L<=R){
		if(L==R&&R==x)return SMIN(ans,t[o].set),ans;
		SMIN(ans,t[o].set);int M=(L+R)>>1;x<=M?(o=lc,R=M):(o=rc,L=M+1);
	}
}
inline void Update(int x,int y,int k){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])x^=y^=x^=y;
		GMIN(1,1,n,dfn[top[x]],dfn[x],k);x=f[top[x]];
	}
	if(dep[x]>dep[y])x^=y^=x^=y;
	x!=y?GMIN(1,1,n,dfn[son[x]],dfn[y],k):(void)0;//错误笔记:一定要有x!=y,不然的话传进去,线段树里面到了最后再pushup会炸 
}

int main(){
#ifndef ONLINE_JUDGE
	freopen("BZOJ2238.in","r",stdin);freopen("BZOJ2238.out","w",stdout);
#endif
	read(n),read(m);for(register int i=1;i<=m;++i)read(G[i].x),read(G[i].y),read(G[i].z),G[i].id=i,G2[i]=G[i];
	Kruskal();read(Q);if(notcon){for(register int i=1;i<=Q;++i)print("Not connected\n");return 0;}//错误笔记:注意询问是Q个不是n个 
	DFS1(1);DFS2(1,1);for(register int i=1;i<=m;++i)if(!intr[i])Update(G2[i].x,G2[i].y,G2[i].z);
	for(register int i=1;i<=Q;++i){//错误笔记:询问数量Q还要另外输入 
		read(x);if(!intr[x]){write(sum),putc('\n');continue;}
		if(dep[G2[x].x]>dep[G2[x].y])G2[x].x^=G2[x].y^=G2[x].x^=G2[x].y;
		int ans=Qmin(1,1,n,dfn[G2[x].y]);if(ans==INF)print("Not connected\n");else write(sum-G2[x].z+ans),putc('\n');
	}
}
posted @ 2018-10-25 17:05  hankeke303  阅读(209)  评论(0编辑  收藏  举报