Prufer序列
对于一棵有n个点,每个点有标号的无根树,我们定义一个Prufer序列与之对应,方便起见,不妨先假设其根为n。具体构造为:找到标号最小的叶子节点,将其从树上删去并将其父亲放入Prufer序列,直到只剩两个点时停止。
指定一个Prufer序列,也可据此构造唯一的树:根据Prufer序列得到每个点的度数,先在prufer序列末尾添加一项n。枚举一个指向Prufer序列的递增的指针i,再找到编号最小的叶子节点(度为1),将其父亲设为当前指针,将两个点的度都-1。不难发现,根据Prufer序列构造树其实是构造Prufer序列的可逆过程,也就是说实质上是在对树删点。那么下一个可能的儿子可能是当前i指向的点,其父亲可能就是Prufer序列的下一项,也就是不断向上跳,i不断自增直到p[i]<=j为止。
两种操作都可以做到
P6086 【模板】Prufer 序列
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const int MAXN=5e6+5;
int n,m;
int a[MAXN],b[MAXN];
int d[MAXN];
ll ftop(){
rep(i,1,n-1)d[a[i]]++;
for(int i=1,j=1;i<=n-2;i++,j++){
while(d[j])j++;
b[i]=a[j];
while(i<=n-2&&!--d[b[i]]&&b[i]<j)b[i+1]=a[b[i]],i++;
}
ll res=0;
rep(i,1,n-2)res^=ll(i)*ll(b[i]);
return res;
}
ll ptof(){
rep(i,1,n-2)d[a[i]]++;
a[n-1]=n;
for(int i=1,j=1;i<=n-1;i++,j++){
while(d[j])j++;
b[j]=a[i];
while(i<=n-1&&!--d[a[i]]&&a[i]<j)b[a[i]]=a[i+1],i++;
}
ll res=0;
rep(i,1,n-1)res^=ll(i)*ll(b[i]);
return res;
}
int main(){
read(n),read(m);
rep(i,1,n-2)read(a[i]);
if(m==1){
read(a[n-1]);
printf("%lld\n",ftop());
}
else printf("%lld\n",ptof());
return 0;
}
性质
- Prufer序列长度为n-2。
- 不难发现,若指定一个点为根,对于每个点,其在Prufer序列中出现的次数是其儿子个数,所以每个点出现次数为该点度数-1,没有出现的就是叶子。
- Prufer序列和树构成一个双射。也就是说任意一个树都有唯一的Prufer序列,而任意一个Prufer序列都有唯一的树与之对应。
应用
由于Prufer序列和树构成一个双射,常用于解决和树的度数有关的计数问题。
克莱定理
对于有标号的n个点的无向完全图,其无根生成树个数为
考虑其Prufer序列,每个位置有n种选择,可以构造
UVA10843 Anne's game
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const ll mod=2000000011;
int n;
ll ksm(ll a,ll e){
ll res=1;
while(e>0){
if(e&1)res=(res*a)%mod;
a=(a*a)%mod;
e>>=1;
}
return res;
}
int main(){
read(n);
rep(i,1,n){
ll x;
read(x);
printf("Case #%d: %lld\n",i,ksm(x,x-2));
}
return 0;
}
杂题
P2290 [HNOI2004]树的计数
给出每个点的度数,求满足情况的有几种无根树。
根据排列组合相关,可以知道是
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
typedef long long ll;
const int MAXN=155;
int n,d[MAXN];
int p[MAXN][MAXN],p1[MAXN],p2[MAXN];
ll ksm(ll a,int e){
ll res=1LL;
while(e){
if(e&1)res*=a;
a*=a;
e>>=1;
}
return res;
}
int main(){
read(n);
int s=0;
rep(i,1,n){
read(d[i]);
if(d[i]<=0&&n!=1){puts("0");return 0;}
s+=d[i];
}
if(s!=2*n-2){puts("0");return 0;}
if(n<=2){puts("1");return 0;}
rep(i,2,n-2){
int t=i,END=sqrt(i);
rep(j,2,END)
while(t%j==0){
p[i][j]++;
t/=j;
}
if(t>1)p[i][t]++;
}
rep(i,2,n-2)
rep(j,2,i)p1[j]+=p[i][j];
rep(i,1,n)
rep(j,2,d[i]-1)
rep(k,2,j)p2[k]+=p[j][k];
ll ans=1LL;
rep(i,2,n-2)ans*=ksm(ll(i),p1[i]-p2[i]);
printf("%lld\n",ans);
return 0;
}
CF156D Clues
给定一个
前置知识:多项式定理,即二项式定理的拓展版。
先定义一个记号
那么多项式定理就是
回到这道题。
设每个连通块大小为
设
化为
利用多项式定理,
即
#include<bits/stdc++.h>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define Rrep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template<class T>void read(T &x){
x=0;int f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c))x=x*10+c-'0',c=getchar();
x*=f;
}
const int MAXN=1e5+5;
typedef long long ll;
int n,m,p,k;
int fa[MAXN],sz[MAXN];
int FIND(int x){
return x==fa[x]?x:fa[x]=FIND(fa[x]);
}
void meg(int x,int y){
int fx=FIND(x),fy=FIND(y);
fa[fy]=fx;
}
ll ksm(ll a,int e){
ll res=1;
while(e){
if(e&1)res=(res*a)%p;
a=(a*a)%p;
e>>=1;
}
return res;
}
int main(){
read(n),read(m),read(p);
rep(i,1,n)fa[i]=i;
int x,y;
rep(i,1,m){
read(x),read(y);
meg(x,y);
}
rep(i,1,n)sz[FIND(i)]++;
ll ans=1;
rep(i,1,n)
if(FIND(i)==i)k++,ans=(ans*sz[i])%p;
if(k==1){printf("%d\n",1%p);return 0;}
ans=(ans*ksm(n,k-2))%p;
printf("%lld\n",ans);
return 0;
}