zoukankan      html  css  js  c++  java
  • 浅谈Prufer序列

    ( ext{Prufer})序列,是树与序列的一种双射。

    构建过程:

    • 每次找到一个编号最小的叶子节点(Leaf),将它删掉,并将它所连接的点的度数(-1),且加入( ext{Prufer})序列。

    • 重复上述步骤,直到只剩下两个点。

    实现:

    考虑如何实现。

    最朴素的显然每次暴力找,复杂度(O(n^2).)显然不够优秀。

    用堆来维护节点,显然可以做到(O(nlog n).)

    考虑线性实现:维护一个指针,每次指向编号最小的叶节点。删除之后,如果新产生了叶节点并且编号要比指针编号小,则加入( ext{Prufer})序列,继续删除。否则自增到下一个叶节点。

    指针只会增加,最多增加(O(n)次),时间复杂度即为(O(n).)

    Rebuild重建

    若已知( ext{Prufer})序列,如何重建树?

    先通过( ext{Prufer})序列将每一个元素的度(( ext{Deg}))还原。

    同样地,维护一个指针,每次指向编号最小的叶子节点,并把它与当前( ext{Prufer})序列的第一个元素相连。将两个元素的度都减一,并继续找新产生的(Leaf),同( ext{Prufer})序列的构建过程。

    时间复杂度同样是(O(n).)

    用处:

    • 凯莱定理:(n)个点的完全图生成树个数为(n^{n-2}.)
    • 用来造树的数据
    • 推一堆计数式子

    例题

    代码实现:

    下面是模板代码,用了( ext{fread.})

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,f[5000010],p[5000010],d[5000010];
    long long ans;
    namespace IO{
    	char buf[1<<21],*p1=buf,*p2=buf;
    	#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    	inline int read(){
    		int s=0,w=1;
    		char ch=gc();
    		while(!isdigit(ch)){
    			if(ch=='-')w=-1;
    			ch=gc();
    		}
    		while(isdigit(ch)){
    			s=s*10+ch-'0';
    			ch=gc();
    		}
    		return w==-1?-s:s;
    	}
    }
    using namespace IO;
    inline void Turn_Prufer(){
    	for(int i=1;i<n;++i)f[i]=read(),++d[f[i]];
    	//j为指针 
    	for(int i=1,j=1;i<n-1;++i,++j){
    		while(d[j])++j;p[i]=f[j];//找到第一个deg为0的点,即为叶子,删掉,把它相连的点加到Prufer里 
    		while(i<n-1&&!--d[p[i]]&&p[i]<j)p[i+1]=f[p[i]],++i;//如果Prufer还没找完,并且删掉它爹(这里新产生的点就是p[i])的度数后也成为叶子,且它的编号要小,此处的p[i]就是上一个点(j)的爹 
    	}
    	for(int i=1;i<n-1;++i)ans^=1ll*i*p[i];
    }
    inline void Turn_Father(){
    	for(int i=1;i<n-1;++i)p[i]=read(),++d[p[i]];
    	p[n-1]=n;
    	for(int i=1,j=1;i<n;++i,++j){
    		while(d[j])++j;f[j]=p[i];
    		while(i<n&&!--d[p[i]]&&p[i]<j)f[p[i]]=p[i+1],++i;//p[i]就是新产生的点,根据Prufer逆向构造即可 
    	}
    	for(int i=1;i<n;++i)ans^=1ll*i*f[i];
    }
    inline void write(long long A){
    	if(A<0)putchar('-'),A=-A;
    	if(A>9)write(A/10);
    	putchar(A%10+'0');
    }
    int main(){
    	n=read(),m=read();
    	if(m==1)Turn_Prufer();
    	else Turn_Father();
    	write(ans);
    	return 0;
    }
    
  • 相关阅读:
    在C#代码中应用Log4Net(二)典型的使用方式
    在C#代码中应用Log4Net(一)简单使用Log4Net
    Windows Azure Active Directory (2) Windows Azure AD基础
    Windows Azure Virtual Network (6) 设置Azure Virtual Machine固定公网IP (Virtual IP Address, VIP) (1)
    Windows Azure Active Directory (1) 前言
    Azure China (6) SAP 应用在华登陆 Windows Azure 公有云
    Microsoft Azure News(3) Azure新的基本实例上线 (Basic Virtual Machine)
    Microsoft Azure News(2) 在Microsoft Azure上运行SAP应用程序
    Microsoft Azure News(1) 新的数据中心Japan East, Japan West and Brazil South
    Windows Azure HandBook (2) Azure China提供的服务
  • 原文地址:https://www.cnblogs.com/h-lka/p/13173203.html
Copyright © 2011-2022 走看看