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;
    }
    
  • 相关阅读:
    Oracle 列转行函数 Listagg()
    JS正则表达式
    云经验
    业务架构、应用架构、数据架构和技术架构
    Sublime Text3+Golang搭建开发环境
    成功安装vscode中go的相关插件
    Visual Studio Code配置GoLang开发环境
    团队架构实践
    架构
    条件引用
  • 原文地址:https://www.cnblogs.com/h-lka/p/13173203.html
Copyright © 2011-2022 走看看