本博客除代码之外,来自 skylee 大佬。
题目大意
一棵\(n(n\le10^5)\)个编号为\(1\sim n\)的点的带边权的树,求一个排列\(p_{1\sim 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\times dep_{lca(i,p_i)})=2\times\sum dep_i-2\times\sum dep_{lca(i,p_i)}\)。而如果我们能够找到一个合适的点作为根,使得\(lca(i,p_i)=1\)则答案最大值即为\(2\times\sum dep_i\)。而通过证明可以发现一个点可以作为根当且仅当这个点是树的重心,证明如下(引自Code仓库):
设\(P\)为重心,若\(P\)不可被当作公共点,设\(T_1\)是\(P\)的大小\(>\lfloor\frac n2\rfloor\)的子树,其根为\(Q\),那么把\(Q\)拔掉的话,包含\(P\)的那棵子树的大小就会\(<n-\lfloor\frac n2\rfloor=\lceil\frac n2\rceil\le\lfloor\frac n2\rfloor+1\le T_1\)的大小,并且把\(Q\)拔掉后的其他子树大小显然都会小于\(T_1\)的大小,因此把\(Q\)拔掉会让剩余的最大子树的大小比把\(P\)拔掉的还要小,则\(P\)不是重心,矛盾。因此重心可以被当作公共点。
再来证明非重心的点不能被当作公共点,一样设\(P\)为重心,并且\(Q\)不是重心,他落在\(P\)的\(T_1\)子树中,那么有\(T_1\)的大小\(\le\lfloor\frac n2\rfloor\),因此整棵树扣掉\(T_1\)的大小\(\ge\lceil\frac n2\rceil\),因此可以得到若\(Q\)想要当公共点,他就必须是\(T_1\)的根,并且满足\(T_1\)的大小刚好是\(\lfloor\frac n2\rfloor\),并且整棵树扣掉\(T_1\)的大小要刚好是\(\lceil\frac n2\rceil\),所以就可以得到\(n\)为偶数,\(T_1\)的大小为\(\frac n2\),所以\(Q\)也是重心,矛盾。
此时我们已经完成了第一问,可以解决HDU4118这个问题。现在考虑第二问,即如何求出字典序最小的\(p\)。
如果定义排列中\(i\)为出点,\(p_i\)为入点,将树上的每一个点拆成一个入点和一个出点,那么题目就变成了一个完全匹配问题。
去掉重心后原图分为\(T_{1\sim 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]\le\sum_{j=1}^r in[j]\),也即\(in[i]+out[i]\)小于此时未匹配的入点个数。若按\(1\sim 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 in[N]
维护每个子树中所有未匹配的入点编号,std::set min
维护每个子树中未匹配的编号最小的入点,std::set> set
记录每个子树中未匹配的入点和出点总数和该子树编号。
时间复杂度\(\mathcal O(n\log n)\)。
源代码
#include<cstdio> #include<set> #include<utility> using namespace std; #define rep(i,__l,__r) for(signed i=__l,i##_end_=__r;i<=i##_end_;++i) #define fep(i,__l,__r) for(signed i=__l,i##_end_=__r;i>=i##_end_;--i) #define writc(a,b) fwrit(a),putchar(b) #define mp(a,b) make_pair(a,b) #define ft first #define sd second #define LL long long #define ull unsigned long long #define uint unsigned int #define pii pair< int,int > #define Endl putchar('\n') // #define FILEOI // #define int long long // #define int unsigned #ifdef FILEOI # define MAXBUFFERSIZE 500000 inline char fgetc(){ static char buf[MAXBUFFERSIZE+5],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXBUFFERSIZE,stdin),p1==p2)?EOF:*p1++; } # undef MAXBUFFERSIZE # define cg (c=fgetc()) #else # define cg (c=getchar()) #endif template<class T>inline void qread(T& x){ char c;bool f=0; while(cg<'0'||'9'<c)f|=(c=='-'); for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48)); if(f)x=-x; } inline int qread(){ int x=0;char c;bool f=0; while(cg<'0'||'9'<c)f|=(c=='-'); for(x=(c^48);'0'<=cg&&c<='9';x=(x<<1)+(x<<3)+(c^48)); return f?-x:x; } // template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);} template<class T>inline T Max(const T x,const T y){return x>y?x:y;} template<class T>inline T Min(const T x,const T y){return x<y?x:y;} template<class T>inline T fab(const T x){return x>0?x:-x;} inline int gcd(const int a,const int b){return b?gcd(b,a%b):a;} inline void getInv(int inv[],const int lim,const int MOD){ inv[0]=inv[1]=1;for(int i=2;i<=lim;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD; } template<class T>void fwrit(const T x){ if(x<0)return (void)(putchar('-'),fwrit(-x)); if(x>9)fwrit(x/10); putchar(x%10^48); } inline LL mulMod(const LL a,const LL b,const LL mod){//long long multiplie_mod return ((a*b-(LL)((long double)a/mod*b+1e-8)*mod)%mod+mod)%mod; } const int MAXN=1e5; struct edge{ int to,nxt,w; edge(const int T=0,const int N=0,const int W=0):to(T),nxt(N),w(W){} }e[(MAXN<<1)+5]; int tail[MAXN+5],ecnt; inline void add_edge(const int u,const int v,const int w){ e[++ecnt]=edge(v,tail[u],w);tail[u]=ecnt; e[++ecnt]=edge(u,tail[v],w);tail[v]=ecnt; } int n,siz[MAXN+5],fa[MAXN+5]; LL dis[MAXN+5],ans1; int rt,tsize=(1<<30)-1; inline void dfs(const int u,const int ff){ siz[u]=1; int maxx=0; for(int i=tail[u],v;i;i=e[i].nxt)if((v=e[i].to)!=ff){ dfs(v,u); siz[u]+=siz[v]; maxx=Max(maxx,siz[v]); } maxx=Max(maxx,n-siz[u]); if(maxx<tsize)rt=u,tsize=maxx; } inline void dfs(const int u){ ans1+=dis[u]; for(int i=tail[u],v;i;i=e[i].nxt)if((v=e[i].to)!=fa[u]){ dis[v]=dis[fa[v]=u]+e[i].w; dfs(v); } } int bel[MAXN+5]; set<int>in[MAXN+5]; //维护每一个子树的入点编号 set<int>minn; //维护 每个子树合法入点的最小值 的最小值 //每个子树最多只会在 minn 中存在一个节点 set< pair<int,int> >Set;//维护 in[tre]+out[tre] 的最小值以及 tre 的值 inline void init_tre(const int u,const int top){//初始化每一颗子树 in[bel[u]=top].insert(u); // printf("Now u == %d, bel[u] == %d\n",u,bel[u]); for(int i=tail[u],v;i;i=e[i].nxt)if((v=e[i].to)!=fa[u]) init_tre(v,top); } inline void link(const int from,const int to){ int x=bel[from],y=bel[to]; minn.erase(to); //为了处理 rt 的 bel==0 的情况 if(x){ Set.erase(mp(siz[x],x)); Set.insert(mp(--siz[x],x)); } if(y){ in[y].erase(to); if(!in[y].empty())minn.insert(*in[y].begin()); Set.erase(mp(siz[y],y)); Set.insert(mp(--siz[y],y)); } } inline int solve(const int ind){ int ret; if(Set.rbegin()->first==n-ind+1 && Set.rbegin()->second!=bel[ind]) ret=*in[Set.rbegin()->second].begin(); else ret=(bel[ind]!=bel[*minn.begin()] || ind==rt)?(*minn.begin()):(*next(minn.begin())); //如果当前点与最小入点不在同一颗子树或者当前点为根, 可直接选取最小入点, 否则要选择下一个 //为什么直接是下一个即可 ? 因为每一颗子树在 minn 里面的点只会有一个, 可以保证 next 一定不是在同一个子树之内的 link(ind,ret); return ret; } signed main(){ #ifdef FILEOI freopen("file.in","r",stdin); freopen("file.out","w",stdout); #endif for(int i=n=qread(),u,v;i>1;--i){ qread(u),qread(v); add_edge(u,v,qread()); } dfs(1,0);//找到树的重心 dfs(rt);//根据重心重新建树 writc(ans1<<1,'\n'); if(n==1)return puts("1"),0; minn.insert(rt); // in[rt].insert(rt); // bel[rt]=rt; // Set.insert(mp(siz[rt]=2,rt)); //根也算一颗单独的子树 //此处 Set 里面不能放 rt //因为 Set 里面维护的是 rt 的子树, 而 rt 本身并不是子树 for(int i=tail[rt],v;i;i=e[i].nxt){//预处理每一颗子树 v=e[i].to; init_tre(v,v); minn.insert(*in[v].begin()); Set.insert(mp(siz[v]=(in[v].size()<<1),v)); }//注意:siz[i] 从此处开始就变为了这个子树中 in+out 的值 rep(i,1,n)writc(solve(i),' '); return 0; }
来源:https://www.cnblogs.com/Arextre/p/12222795.html