【XSY3163】Tree Ext【二分】【最小生成树】【矩阵树定理】【拉格朗日插值】

给一张 n 个点 m 条边的无向连通图,每条边是黑边或白边 ,有边权。问有多少棵恰好有k条白边,且在此前提下边权和最小的生成树。mod 1e9+7。
首先看一看这道题。
ACM Live Archieve 7138
这里有一个无比良心的题解。
这里写链接内容
膜拜这位不知名的ACM大神!
由于发现博主已经两年多没更新了,这个网站可能会隔,蒟蒻截了张图。。。如果侵权,马上删除。
这里写图片描述
对于恰好k条白边的要求,我们可以二分一个权,给每条白边加上这个权,再最小生成树,注意相同边权白边排前面,如果最小生成树中白边数量>=k判定为可行,否则判定为不可行。
这不就是bzoj2654吗?
最后这题要求的是最小生成树计数,不是生成树计数。不过我们可以通过乘法原理和分段的思想转化为多次生成树计数。这不就是bzoj1016吗?
因为某些玄学bug一直WA30分QAQ
注意可能相同边权的边全部连了之后,各个联通块仍然不连通,所以要提前在他们之间连一些黑边,不影响答案。
题解链接
其实是把三道题强行拼在一起qwq

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cassert>
#define int long long
using namespace std;
const int N=105,M=10005,mod=1000000007;
int n,m,k,l,r,mid,tot,fa[N],pa[N],res[N],ans[N],tmp[N],id[N],num[N],x[N],y[N];
struct edge{
    int u,v,d,c;
}e[M];
bool cmp(edge a,edge b){
    return a.d==b.d?a.c<b.c:a.d<b.d;
}
int find(int u){
    return u==fa[u]?u:fa[u]=find(fa[u]);
}
int get(int u){
    return u==pa[u]?u:pa[u]=get(pa[u]);
}
bool check(){
    for(int i=1;i<=m;i++){
        if(!e[i].c){
            e[i].d+=mid;
        }
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
    int cnt=0;
    for(int i=1,j=0;j<n-1;i++){
        int u=find(e[i].u),v=find(e[i].v);
        if(u!=v){
            fa[v]=u;
            j++;
            if(!e[i].c){
                cnt++;
            }
        }
    }
    for(int i=1;i<=m;i++){
        if(!e[i].c){
            e[i].d-=mid;
        }
    }
    return cnt>=k;
}
int fastpow(int a,int x){
    a%=mod;
    int res=1;
    while(x){
        if(x&1){
            res=res*a%mod;
        }
        x>>=1;
        a=a*a%mod;
    }
    return res;
}
int solve(int l,int r,int x){
    static int a[N][N];
    memset(a,0,sizeof(a));
    for(int i=l;i<=r;i++){
        int u=id[find(e[i].u)],v=id[find(e[i].v)];
        if(u==v){
            continue;
        }
        if(e[i].c){
            a[u][u]++;
            a[v][v]++;
            a[u][v]--;
            a[v][u]--;
        }else{
            a[u][u]+=x;
            a[v][v]+=x;
            a[u][v]-=x;
            a[v][u]-=x;
        }
    }
    memcpy(pa,fa,sizeof(fa));
    for(int i=2;i<=tot;i++){
        int u=get(num[i]),v=get(num[i-1]);
        if(u!=v){
            pa[v]=u;
            a[i][i]++;
            a[i-1][i-1]++;
            a[i][i-1]--;
            a[i-1][i]--;
        }
    }
    for(int i=1;i<=tot;i++){
        for(int j=1;j<=tot;j++){
            a[i][j]=(a[i][j]%mod+mod)%mod;
        }
    }
    int res=1;
    for(int i=1;i<tot;i++){
        for(int j=i+1;j<tot;j++){
            if(a[j][i]){
                int t=a[i][i]*fastpow(a[j][i],mod-2)%mod,tmp;
                for(int k=i;k<tot;k++){
                    tmp=(a[i][k]-a[j][k]*t%mod+mod)%mod;
                    a[i][k]=a[j][k];
                    a[j][k]=tmp;
                }
                res=-res;
            }
        }
    }
    if(res==-1){
        res+=mod;
    }
    for(int i=1;i<tot;i++){
        res=res*a[i][i]%mod;
    }
    return res;
}
void lagerange(){
    static int a[N],b[N],c[N];
    memset(a,0,sizeof(a));
    memset(b,0,sizeof(b));
    memset(c,0,sizeof(c));
    memset(res,0,sizeof(res));
    a[0]=1;
    for(int i=0;i<=tot;i++){
        b[0]=0;
        for(int j=1;j<=i+1;j++){
            b[j]=a[j-1];
        }
        for(int j=0;j<=i;j++){
            b[j]=(b[j]-x[i]*a[j]%mod+mod)%mod;
        }
        for(int j=0;j<=i+1;j++){
            a[j]=b[j];
        }
    }
    for(int i=0;i<=tot;i++){
        for(int j=0;j<=tot+1;j++){
            b[j]=a[j];
        }
        for(int j=tot+1;j>=1;j--){
            c[j-1]=b[j];
            b[j-1]=(b[j-1]+1LL*b[j]*x[i]%mod)%mod;
        }
        int tmp=1;
        for(int j=0;j<=tot;j++){
            if(j!=i){
                tmp=tmp*(x[i]-x[j]+mod)%mod;
            }
        }
        tmp=y[i]*fastpow(tmp,mod-2)%mod;
        for(int j=0;j<=tot;j++){
            c[j]=c[j]*tmp%mod;
            res[j]=(res[j]+c[j])%mod;
        }
    }
}
signed main(){
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=m;i++){
        scanf("%lld%lld%lld%lld",&e[i].u,&e[i].v,&e[i].d,&e[i].c);
    }
    l=-1e9-1,r=1e9+1;
    while(l<r){
        mid=(l+r+1)/2;
        if(check()){
            l=mid;
        }else{
            r=mid-1;
        }
    }
    for(int i=1;i<=m;i++){
        if(!e[i].c){
            e[i].d+=l;
        }
    }
    sort(e+1,e+m+1,cmp);
    for(int i=1;i<=n;i++){
        fa[i]=i;
    }
    ans[0]=1;
    for(l=1;l<=m;l=r+1){
        r=l;
        while(r<m&&e[r+1].d==e[l].d){
            r++;
        }
        memset(id,0,sizeof(id));
        tot=0;
        for(int i=l;i<=r;i++){
            int u=find(e[i].u),v=find(e[i].v);
            if(u!=v){
                if(!id[u]){
                    id[u]=++tot;
                    num[tot]=u;
                }
                if(!id[v]){
                    id[v]=++tot;
                    num[tot]=v;
                }
            }
        }
        if(!tot||tot==1){
            continue;
        }
        memcpy(pa,fa,sizeof(fa));
        for(int i=l;i<=r;i++){
            int u=find(e[i].u),v=find(e[i].v);
            if(u!=v){
                fa[v]=u;
            }
        }
        for(int i=0;i<=tot;i++){
            x[i]=i+1;
            y[i]=solve(l,r,i+1);
        }
        lagerange();
        memset(tmp,0,sizeof(tmp));
        for(int i=0;i<n;i++){
            for(int j=0;i+j<n;j++){
                tmp[i+j]+=ans[i]*res[j]%mod;
                tmp[i+j]%=mod;
            }
        }
        memcpy(ans,tmp,sizeof(tmp));
    }
    for(int i=2;i<=n;i++){
        if(find(i)!=find(1)){
            puts("0");
            return 0;
        }
    }
    printf("%lld\n",ans[k]);
    return 0;
}
posted @ 2018-08-14 07:49  ez_2016gdgzoi471  阅读(305)  评论(0编辑  收藏  举报