P4208 [JSOI2008]最小生成树计数

Posted y2823774827y

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P4208 [JSOI2008]最小生成树计数相关的知识,希望对你有一定的参考价值。

题目

P4208 [JSOI2008]最小生成树计数

以前一直以为JSOI的题应该比较水吧,果然还是我太ruo了

做法

两个性质
(1.):最小生成树中同一权值的边数一定
(2.):最小生成树中同一权值的边连接后联通块状态相同

利用这两个性质,开两个数组维护所属联通块(并查集),每次处理同一边权的点

假设我们上一次权值的记录为(last),实时更新的为(fa)

处理此权值边:如果在last中的两点没联通那标记一下这是可用的,让这些点形成树,存在有些在处理此边权时联通,会形成环,这些不是不能选,因为我们用的方法是分治形成树,用这些边单独形成一棵树,显然所有权值的边最后会形成一棵大树,所以我们这里记录祖先是用的联通之前的,然后更新祖先是实时更新的(可以理解为处理一个权值后就把那些缩点)

然后处理同一权值,这些块可能不止一个就分开计算这些边能形成的树,矩阵树就行了

My complete code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<ctime>
#include<vector>
using namespace std;
typedef int LL;
const LL maxn=200;
const LL p=31011;
inline LL Read(){
    LL x(0),f(1); char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') f=-1; c=getchar();
    }
    while(c>='0'&&c<='9')
        x=(x<<3)+(x<<1)+c-'0',c=getchar();
    return x*f;
}
struct node{
    LL u,v,d;
}e[maxn*maxn];
LL n,m;
LL g[maxn][maxn],D[maxn][maxn];
inline bool cmp(node x,node y){
    return x.d<y.d;
}
LL Get_fa(LL u,LL *fa){
    return fa[u]=(fa[u]==u)?u:Get_fa(fa[u],fa);
}
inline LL Calc(LL N){
    LL tr(0),ret(1);
    for(LL i=1;i<=N;++i){
        for(LL j=i+1;j<=N;++j){
            while(D[j][i]){
                LL tmp=D[i][i]/D[j][i];
                for(LL k=1;k<=N;++k){
                    D[i][k]=(D[i][k]-tmp*D[j][k]%p+p)%p;
                    swap(D[i][k],D[j][k]);
                }
                tr^=1;
            }
        }
        if(D[i][i]==0)
            return 0;
        ret=ret*D[i][i]%p;
    }
    if(tr)
        ret=p-ret;
    return ret;
}
LL ans=1;
LL fa[maxn],last[maxn];
bool visit[maxn];
vector<LL> belong[maxn];
inline void Solve(){
    for(LL i=1;i<=n;++i)
        if(visit[i])
            belong[Get_fa(i,fa)].push_back(i),
            visit[i]=false;
    LL ret(1);
    for(LL i=1;i<=n;++i){
        LL size(belong[i].size());
        if(size<=1)
            continue;
        memset(D,0,sizeof(D));
        for(LL j=1;j<=size;++j){
            LL u(belong[i][j-1]);
            for(LL k=j+1;k<=size;++k){
                LL v(belong[i][k-1]);
                if(g[u][v]){
                    D[j][k]=D[k][j]=(p-g[u][v])%p,
                    D[j][j]+=g[u][v],D[k][k]+=g[u][v];
                }
            }
        }
        ans=(ans*Calc(size-1))%p;
        for(LL j=1;j<=size;++j)
            last[belong[i][j-1]]=i;
    }
    for(LL i=1;i<=n;++i)
        belong[i].clear(),
        fa[i]=last[i]=Get_fa(i,last);
}
int main(){
    n=Read(),m=Read();
    for(LL i=1;i<=m;++i)
        e[i].u=Read(),e[i].v=Read(),e[i].d=Read();
    sort(e+1,e+1+m,cmp);
    e[0]=e[1];
    for(LL i=1;i<=n;++i)
        last[i]=fa[i]=i;
    for(LL i=1;i<=m;++i){
        if(e[i].d^e[i-1].d){
            Solve();
            memset(g,0,sizeof(g));
        }
        LL u(e[i].u),v(e[i].v);
        LL fu(Get_fa(u,last)),fv(Get_fa(v,last)) ;
        if(fu^fv){
            visit[fu]=visit[fv]=true;
            ++g[fu][fv]; ++g[fv][fu];
            fa[Get_fa(fu,fa)]=Get_fa(fv,fa);
        }
    }Solve();
    for(LL i=2;i<=n;++i)
        if(last[i]^last[i-1]){
            printf("0");
            return 0;
        }
    printf("%d",ans);
    return 0;
}/*
*/

以上是关于P4208 [JSOI2008]最小生成树计数的主要内容,如果未能解决你的问题,请参考以下文章

P4208 [JSOI2008]最小生成树计数

P4208 [JSOI2008]最小生成树计数

P4208 [JSOI2008]最小生成树计数

P4208 [JSOI2008]最小生成树计数

luogu4208

BZOJ 1016: [JSOI2008]最小生成树计数