51Nod1601 完全图的最小生成树计数

题目看这里

这个题好像在哪里做过。。。但是翻不到

基本思想:在最高位不同的两个集合里只能有一条边相连

我们可以用trie来做,每次到一个节点,就在他的两个儿子里找xor值最小的加到答案里

若有超过2个权值相同的点时,计算方案的方法为x^(x-2),这个是完全图的生成树个数公式

无压力·真rank1

#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define M 1000000007
#define LL long long
using namespace std;
int s[N<<5][2],v[N],n,m,cnt=1,c[N<<5],m1;
LL S=0,A=1,c1;
inline void pow(LL x,LL k,LL& s){
    for(;k>0;x=x*x%M,k>>=1) k&1?s=s*x%M:0;
}
inline void insert(int v){
    int x=1;
    for(int i=29;~i;--i)
        x=(s[x][(v>>i)&1]?s[x][(v>>i)&1]:s[x][(v>>i)&1]=++cnt);
    ++c[x];
}
void gmin(int x,int y,int d,int v=0){
    if(d<0){
        if(v<m1){ m1=v; c1=(LL)c[x]*c[y]%M; }
        else if(v==m1) c1=(c1+(LL)c[x]*c[y]%M)%M;
    }
    if(s[x][0] && s[y][0]){
        gmin(s[x][0],s[y][0],d-1,v);
        if(s[x][1] && s[y][1]) gmin(s[x][1],s[y][1],d-1,v);
    } else if(s[x][1] && s[y][1]) gmin(s[x][1],s[y][1],d-1,v);
    else{
        if(s[x][0]) gmin(s[x][0],s[y][1],d-1,v+(1<<d));
        if(s[x][1]) gmin(s[x][1],s[y][0],d-1,v+(1<<d));
    }
}
void solve(int x,int d){
    if(d<0){ if(c[x]>2) pow(c[x],c[x]-2,A); return; }
    if(!s[x][0]) solve(s[x][1],d-1); else 
    if(!s[x][1]) solve(s[x][0],d-1); else{
        solve(s[x][0],d-1);
        solve(s[x][1],d-1);
        m1=1<<30; c1=0;
        gmin(s[x][0],s[x][1],d-1);
        S=S+(1ll<<d)+m1;
        A=A*c1%M;
    }
}
int main(){
    scanf("%d",&n);
    for(int x,i=1;i<=n;++i) 
        scanf("%d",&x),insert(x);
    solve(1,29);
    printf("%lld
%lld",S,A);
}

原文地址:https://www.cnblogs.com/Extended-Ash/p/9477121.html