HDU3710-Battle Over Cities

题意

给出一个\(n\)个点\(m\)条边的无向连通图,问删掉每一个点后的最小生成树权值和为多少(如果不存在最下生成树就输出inf)。

\(n\le 2\times 10^4,m\le 10^5\)

分析

换了个超级爽的青轴键盘后写题就开始加速了啊!这样非常不好!!!这道题在写的时候出了很多问题,以后还是要慢慢想清楚再写。

首先一个图的生成树(特别是最小生成树)这种问题,我们可以先把整体的最小生成树建出来。下面就是看删掉一个点会导致什么结果。

把生成树看成一颗有根树,那么删掉一个点会让它的子树,以及父亲节点互相分离。我们要把它用最小的代价连回去。这里的连边只有两种,子树之间的“横向边”和子树连到外面的“纵向边”。

子树内的边的处理基于一条\((x,y)\)边为“子树之间”的边,只有在它们的lca处才会出现一次这样的情况(如果在lca的上面那么它们就属于同一颗子树了)。这样我们就可以把这些边加到数组里。

注意到子树连到外面的多条边中,只有最小的那条是有用的,我们只要想办法求出一个子树连到外面的所有边中最小的就可以了。这可以用线段树合并(dfn序),可并堆(当一条边在一个子树中就pop),树链剖分(考虑在哪些点一条边为“纵向边”,显然是两点之间的路径上除了lca之外的所有点),这几种方法来实现。

这样我们就保证了总的求最小生成树的边数为\(O(n+m)\),所以可以在\(O(n+m\log (n+m))\)的时间内解决这个问题。

这里的子树之间的情况分析在别的题中也有广泛的应用。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<vector>
#include<utility>
#include<algorithm>
#define M(x) memset(x,0,sizeof x)
using namespace std;
int read() {
	int x=0,f=1;
	char c=getchar();
	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*f;
}
const int maxn=2e4+1;
const int maxm=1e5+1;
const int nlogn=3e6;
const int maxj=15;
const int inf=1e9+7;
typedef pair<int,int> Pair;
Pair operator + (Pair a,Pair b) {
	return min(a,b);
}
struct bian {
	int x,y,w;
	bool alr;
	bian (int x=0,int y=0,int w=0):alr(false),x(x),y(y),w(w) {}
	inline bool operator < (const bian &a) const {return w<a.w;}
} b[maxm];
int with[maxn],ans[maxn],n,m,base,root[maxn];
namespace uns {
	int f[maxn];
	void clear(int n) {for (int i=1;i<=n;++i) f[i]=i;}
	int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
}
namespace sgt {
	struct node {
		int l,r;
		Pair dat;
	} t[nlogn]; 
	int tot;
	void update(int x) {
		if (t[x].l) t[x].dat=t[x].dat+t[t[x].l].dat;
		if (t[x].r) t[x].dat=t[x].dat+t[t[x].r].dat;
	}
	int merge(int x,int y,int l,int r) {
		if (!x) return y;
		if (!y) return x;
		if (l==r) {
			t[x].dat=t[x].dat+t[y].dat;
			return x;
		}
		int mid=(l+r)>>1;
		t[x].l=merge(t[x].l,t[y].l,l,mid);
		t[x].r=merge(t[x].r,t[y].r,mid+1,r);
		update(x);
		return x;
	}
	Pair query(int x,int L,int R,int l,int r) {
		if (!x) return make_pair(inf,0);
		if (L==l && R==r) return t[x].dat;
		int mid=(L+R)>>1;
		if (r<=mid) return query(t[x].l,L,mid,l,r);
		if (l>mid) return query(t[x].r,mid+1,R,l,r);
		return query(t[x].l,L,mid,l,mid)+query(t[x].r,mid+1,R,mid+1,r);
	}
	Pair query(int x,int l,int r) {
		if (l>r) return make_pair(inf,0);
		return query(x,1,n,l,r);
	}
	void modify(int &x,int l,int r,int p,Pair d) {
		if (!x) t[x=++tot]=(node){0,0,make_pair(inf,0)};
		if (l==r) {
			t[x].dat=t[x].dat+d;
			return;
		}
		int mid=(l+r)>>1;
		p<=mid?modify(t[x].l,l,mid,p,d):modify(t[x].r,mid+1,r,p,d);
		update(x);
	}
	void modify(int &x,int p,Pair d) {
		modify(x,1,n,p,d);
	}
}
namespace tree {
	vector<int> g[maxn];
	vector<bian> on[maxn];
	int first[maxn],second[maxn],dfx,f[maxn][maxj],dep[maxn];
	int id[maxn];
	int getid(int x) {return id[x]==x?x:id[x]=getid(id[x]);}
	void clear(int n) {
		M(first),M(second),M(f),M(dep),dfx=0;
		for (int i=1;i<=n;++i) id[i]=i;
		for (int i=1;i<=n;++i) g[i].clear(),on[i].clear();
	}
	void add(int x,int y) {g[x].push_back(y);}
	void dfs(int x,int fa) {
		f[x][0]=fa;
		dep[x]=dep[fa]+1;
		first[x]=++dfx;
		for (int v:g[x]) if (v!=fa) dfs(v,x);
		second[x]=dfx;
	}
	void run() {
		for (int j=1;j<maxj;++j) for (int i=1;i<=n;++i) f[i][j]=f[f[i][j-1]][j-1];
	}
	int lca(int x,int y) {
		if (dep[x]<dep[y]) swap(x,y);
		for (int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
		if (x==y) return x;
		for (int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
		return f[x][0];
	}
	void work(int x,int fa) {
		vector<bian> &e=on[x];
		int gs=(x!=1);
		for (int v:g[x]) if (v!=fa) {
			work(v,x);
			++gs;
			uns::f[v]=v;
			if (x==1) continue;
			Pair ret1=sgt::query(root[v],1,first[x]-1);
			Pair ret2=sgt::query(root[v],second[x]+1,n);
			Pair ret=ret1+ret2;
			if (ret.first<inf) e.push_back((bian){x,v,ret.first});
			root[x]=sgt::merge(root[x],root[v],1,n);
		}
		uns::f[x]=x;
		sort(e.begin(),e.end());
		int &tmp=ans[x]=base-with[x],j=0;
		for (int i=0;i<e.size() && j<gs-1;++i) {
			int u=getid(e[i].x),v=getid(e[i].y),w=e[i].w;
			int fx=uns::find(u),fy=uns::find(v);
			if (fx!=fy) {
				uns::f[fx]=fy;
				++j;
				tmp+=w;
			}
		}
		if (j<gs-1) tmp=-1;
		for (int v:g[x]) if (v!=fa) id[v]=x;
	}
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	int T=read();
	while (T--) {
		n=read(),m=read();
		base=0;
		M(root),M(with);
		tree::clear(n);
		uns::clear(n);
		for (int i=1;i<=m;++i) {
			int x=read(),y=read(),d=read(),c=read(),w=d*(1-c);
			b[i]=(bian){x,y,w};
		}
		sort(b+1,b+m+1);
		for (int i=1,j=0;j<n-1 && i<=m;++i) {
			int x=b[i].x,y=b[i].y,w=b[i].w;
			int fx=uns::find(x),fy=uns::find(y);
			if (fx!=fy) {
				++j;
				tree::add(x,y),tree::add(y,x);
				uns::f[fx]=fy;
				with[x]+=w,with[y]+=w;
				b[i].alr=true;
				base+=w;
			}
		}
		tree::dfs(1,1);
		tree::run();
		sgt::tot=0;
		for (int i=1;i<=m;++i) if (!b[i].alr) {
			int x=b[i].x,y=b[i].y,w=b[i].w;
			if (tree::dep[x]>tree::dep[y]) swap(x,y);
			int l=tree::lca(x,y);
			if (x!=l) tree::on[l].push_back(b[i]);
			sgt::modify(root[y],tree::first[x],make_pair(w,y));
			sgt::modify(root[x],tree::first[y],make_pair(w,x));
		} 
		tree::work(1,1);
		for (int i=1;i<=n;++i) ans[i]==-1?puts("inf"):printf("%d\n",ans[i]);
	}
	return 0;
}
posted @ 2017-07-15 21:24  permui  阅读(417)  评论(0编辑  收藏  举报