zoukankan      html  css  js  c++  java
  • [CF468D]Tree

    [CF468D]Tree

    题目大意:

    ​ 一棵(n(nle10^5))个编号为(1sim n)的点的带边权的树,求一个排列(p_{1sim n}),使(sum dis(i,p_i))最大。求最大化的(sum dis(i,p_i))以及字典序最小的(p)

    思路:

    ​ 考虑第一问。用(dis(x))表示点(x)到根的距离。则不难发现(sum dis(i,p_i)=sum(dep_i+dep_{p_i}-2 imes dep_{lca(i,p_i)})=2 imessum dep_i-2 imessum dep_{lca(i,p_i)})。而如果我们能够找到一个合适的点作为根,使得(lca(i,p_i)=1)则答案最大值即为(2 imessum dep_i)。而通过证明可以发现一个点可以作为根当且仅当这个点是树的重心,证明如下(引自Code仓库):

    (P)为重心,若(P)不可被当作公共点,设(T_1)(P)的大小(>lfloorfrac n2 floor)的子树,其根为(Q),那么把(Q)拔掉的话,包含(P)的那棵子树的大小就会(<n−lfloorfrac n2 floor=lceilfrac n2 ceillelfloorfrac n2 floor+1le T_1)的大小,并且把(Q)拔掉后的其他子树大小显然都会小于(T_1)的大小,因此把(Q)拔掉会让剩余的最大子树的大小比把(P)拔掉的还要小,则(P)不是重心,矛盾。因此重心可以被当作公共点。

    再来证明非重心的点不能被当作公共点,一样设(P)为重心,并且(Q)不是重心,他落在(P)(T_1)子树中,那么有(T_1)的大小(lelfloorfrac n2 floor),因此整棵树扣掉(T_1)的大小(gelceilfrac n2 ceil),因此可以得到若(Q)想要当公共点,他就必须是(T_1)的根,并且满足(T_1)的大小刚好是(lfloorfrac n2 floor),并且整棵树扣掉(T_1)的大小要刚好是(lceilfrac n2 ceil),所以就可以得到(n)为偶数,(T_1)的大小为(frac n2),所以(Q)也是重心,矛盾。

    ​ 此时我们已经完成了第一问,可以解决HDU4118这个问题。现在考虑第二问,即如何求出字典序最小的(p)

    ​ 如果定义排列中(i)为出点,(p_i)为入点,将树上的每一个点拆成一个入点和一个出点,那么题目就变成了一个完全匹配问题。去掉重心后原图分为(T_{1sim r})(r)个子树,记子树(T_i)中有(in[i])个未匹配的入点,(out[i])个未匹配的出点,显然初始状态(in[i]=out[i]=size(T_i))。由于每个出点都要匹配不同一个子树的一个入点,则(out[i]le in[1]+ldots+in[i-1]+in[i+1]+ldots+in[r]),即(in[i]+out[i]lesum_{j=1}^r in[j]),也即(in[i]+out[i])小于此时未匹配的入点个数。若按(1sim n)的顺序求(p_i),则对于每一时刻,对于每一棵子树(T_j),都有(in[j]+out[j]le n-i+1)

    ​ 若存在子树(T_j),满足(in[j]+out[j]=n-i+1),则(p_i)必须在(T_j)中取,因为要保证字典序最小,将(T_j)中最小的入点作为(p_i)即可。若不存在这样的(T_j),则可以从任意一个不同于(i)所属子树的子树中选取最小值。

    ​ 这些最小值可以通过线段树、红黑树、二叉堆等数据结构来维护。考虑使用std::set(红黑树),用std::set<int> in[N]维护每个子树中所有未匹配的入点编号,std::set<int> min维护每个子树中未匹配的编号最小的入点,std::set<std::pair<int,int>> set记录每个子树中未匹配的入点和出点总数和该子树编号。时间复杂度(mathcal O(nlog n))

    源代码:

    #include<set>
    #include<cstdio>
    #include<cctype>
    #include<climits>
    #include<forward_list>
    using int64=long long;
    inline int getint() {
    	register char ch;
    	while(!isdigit(ch=getchar()));
    	register int x=ch^'0';
    	while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
    	return x;
    }
    constexpr int N=1e5+1;
    using Edge=std::pair<int,int>;
    std::forward_list<Edge> e[N];
    inline void add_edge(const int &u,const int &v,const int &w) {
    	e[u].push_front({v,w});
    	e[v].push_front({u,w});
    }
    int n,m,size[N],par[N],bel[N],max[N]={INT_MAX},sum[N],cen;
    int64 dis[N],ans;
    void dfs(const int &x,const int &par) {
    	size[x]=1;
    	for(auto &j:e[x]) {
    		const int &y=j.first;
    		if(y==par) continue;
    		dfs(y,x);
    		size[x]+=size[y];
    		max[x]=std::max(max[x],size[y]);
    	}
    	max[x]=std::max(max[x],n-size[x]);
    	if(max[x]<max[cen]) cen=x;
    }
    void dfs(const int &x) {
    	ans+=dis[x];
    	for(auto &j:e[x]) {
    		const int &y=j.first,&w=j.second;
    		if(y==par[x]) continue;
    		dis[y]=dis[par[y]=x]+w;
    		dfs(y);
    	}
    }
    std::set<int> in[N];
    std::set<int> min;
    std::set<std::pair<int,int>> set;
    void work(const int &x,const int &root) {
    	in[bel[x]=root].insert(x);
    	for(auto &j:e[x]) {
    		const int &y=j.first;
    		if(y==par[x]) continue;
    		work(y,root);
    	}
    }
    inline void link(const int &x,const int &y) {
    	const int &p=bel[x],&q=bel[y];
    	min.erase(y);
    	if(p) {
    		set.erase({sum[p],p});
    		set.insert({--sum[p],p});
    	}
    	if(q) {	
    		in[q].erase(y);
    		if(!in[q].empty()) min.insert(*in[q].begin());
    		set.erase({sum[q],q});
    		set.insert({--sum[q],q});
    	}
    }
    inline int solve(const int &x) {
    	int ret;
    	if(set.rbegin()->first==n-x+1&&set.rbegin()->second!=bel[x]) {
    		ret=*in[set.rbegin()->second].begin();
    	} else {
    		ret=bel[*min.begin()]!=bel[x]||x==cen?*min.begin():*std::next(min.begin());
    	}
    	link(x,ret);
    	return ret;
    }
    int main() {
    	for(register int i=n=getint();i>1;i--) {
    		const int u=getint(),v=getint();
    		add_edge(u,v,getint());
    	}
    	dfs(1,0);
    	dfs(cen);
    	printf("%lld
    ",ans*2);
    	if(n==1) {
    		puts("1");
    		return 0;
    	}
    	min.insert(cen);
    	for(register auto &j:e[cen]) {
    		const int &x=j.first;
    		work(x,x);
    		min.insert(*in[x].begin());
    		set.insert({sum[x]=in[x].size()*2,x});
    	}
    	for(register int i=1;i<=n;i++) {
    		printf("%d%c",solve(i)," 
    "[i==n]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    day26
    day 25
    java.io.IOException: java.net.ConnectException: Call From master/192.168.58.128 to master:10020 failed on connection exception: java.net.ConnectException: 拒绝连接;
    疫情可视化系统
    使用eclipse创建spring cloud的eureka客户端和eureka服务端
    连接虚拟机的hive时进程自动杀死
    在Ubuntu18.04的Docker中安装Oracle镜像及简单使用
    Ubuntu16.04 上Docker 中安装SQL Server 2017
    docker
    Docker镜像报错
  • 原文地址:https://www.cnblogs.com/skylee03/p/9076395.html
Copyright © 2011-2022 走看看