zoukankan      html  css  js  c++  java
  • HDU3710-Battle Over Cities

    题意

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

    (nle 2 imes 10^4,mle 10^5)

    分析

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

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

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

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

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

    这样我们就保证了总的求最小生成树的边数为(O(n+m)),所以可以在(O(n+mlog (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
    ",ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    38
    37
    学记
    36.java_exception_test
    c++中enum的用法——枚举类型
    35
    34
    33
    32
    31
  • 原文地址:https://www.cnblogs.com/owenyu/p/7185759.html
Copyright © 2011-2022 走看看