zoukankan      html  css  js  c++  java
  • P6624-[省选联考2020A卷]作业题【矩阵树定理,欧拉反演】

    正题

    题目链接:https://www.luogu.com.cn/problem/P6624


    题目大意

    (n)个点的一张图,每条边有权值,一棵生成树的权值是所有边权和乘上边权的(gcd),即

    [val(T)=left(sumlimits_{i=1}^{n-1} w_{e_i} ight) imes gcd(w_{e_1},w_{e_2},dots,w_{e_{n-1}}) ]

    求所有生成树的权值和


    解题思路

    首先要知道一个东西(varphi*I=id),于是我们就有

    [gcd(w_1,w_2,dots,w_n)=sum_{d|w_1,d|w_2,dots,d|w_n}varphi(d) ]

    然后化进这个式子里就是

    [left(sumlimits_{i=1}^{n-1} w_{i} ight) imes sum_{d|w_1,d|w_2,dots,d|w_n}varphi(d) ]

    然后把(varphi(d))丢出去就是

    [sum_{d=1}^nvarphi(d) imesleft(sumlimits_{i=1,d|w_i}^{n-1} w_{i} ight) ]

    之后就是怎么快速算后面那个东西的事情了。

    一个比较朴素的做法是枚举每一条边产生的贡献,然后固定这条边然后矩阵树求一次答案乘上这条边就好了。

    但是这个比较慢,我们可以利用生成函数的思想,每一条边权变为(F_i(x)=w_ix+1)的一个多项式,然后所有多项式乘起来之后的一次项系数就是答案了。因为这样只会选择一条边的(w_i)

    多项式除法的话用的比较少,这里化一下

    [frac{ax+b}{cx+d}=zx+wRightarrow ax+b=zcx^2+(zd+cw)x+wd ]

    然后二次项我们直接丢掉就有

    [b=wdRightarrow w=frac{b}{d} ]

    带进后面那个去就有

    [a=zd+cfrac{b}{d}Rightarrow z=frac{ad-cb}{d^2} ]

    所以

    [frac{ax+b}{cx+d}=frac{ad-cb}{d^2}x+frac{b}{d} ]

    这样时间复杂度是(O( max{w_i}(m+n^3) ))的,好像过不了,可以把边数不够(n-1)的直接不管,这样可以省去很多无用情况


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=2e5+10,P=998244353;
    struct fuc{
        ll x,y;
        fuc(ll xx=0,ll yy=0)
        {x=xx;y=yy;return;}
    };
    ll power(ll x,ll b){
        ll ans=1;x%=P;
        while(b){
            if(b&1)ans=ans*x%P;
            x=x*x%P;b>>=1;
        }
        return ans;
    };
    ll inv(ll x){return power(x,P-2);}
    fuc operator+(fuc a,fuc b)
    {return fuc((a.x+b.x)%P,(a.y+b.y)%P);}
    fuc operator-(fuc a,fuc b)
    {return fuc((a.x-b.x+P)%P,(a.y-b.y+P)%P);}
    fuc operator*(fuc a,fuc b)
    {return fuc((a.x*b.y+a.y*b.x)%P,a.y*b.y%P);}
    fuc operator/(fuc a,fuc b)
    {return fuc((a.x*b.y-b.x*a.y+P)%P*inv(b.y*b.y)%P,a.y*inv(b.y)%P);}
    ll n,m,ans,x[N],y[N],w[N],phi[N],pri[N],cnt;
    bool v[N];fuc a[31][31];
    fuc det(){
        fuc ans(0,1);ll f=1;
        for(ll i=1;i<n;i++){
            ll w=i;
            for(ll j=i;j<n;j++)
                if(a[i][j].y){
                    if(i!=j)f=-f;
                    w=j;break;
                }
            swap(a[i],a[w]);
            if(!a[i][i].y)return fuc(0,0);
            ans=ans*a[i][i];
            fuc inv=fuc(0,1)/a[i][i];
            for(ll j=n-1;j>=i;j--)
                a[i][j]=a[i][j]*inv;
            for(ll j=i+1;j<n;j++){
                fuc rate=a[j][i];
                for(ll k=i;k<n;k++)
                    a[j][k]=a[j][k]-rate*a[i][k];
            }
        }
        return (ans.x*f+P)%P;
    }
    void init(ll n){
        phi[1]=1;
        for(ll i=2;i<=n;i++){
            if(!v[i])pri[++cnt]=i,phi[i]=i-1;
            for(ll j=1;j<=cnt&&i*pri[j]<=n;j++){
                v[i*pri[j]]=1;
                if(i%pri[j]==0){
                    phi[i*pri[j]]=phi[i]*pri[j];
                    break;
                }
                phi[i*pri[j]]=phi[i]*phi[pri[j]];
            }
        }
        return;
    }
    signed main()
    {
        scanf("%lld%lld",&n,&m);
        ll mx=0;
        for(ll i=1;i<=m;i++){
            scanf("%lld%lld%lld",&x[i],&y[i],&w[i]);
            x[i]--;y[i]--;mx=max(mx,w[i]);
        }
        init(mx);
        for(ll i=1;i<=mx;i++){
            ll cnt=0;
            for(ll j=1;j<=m;j++)
                if(w[j]%i==0)cnt++;
            if(cnt<n-1)continue;
            memset(a,0,sizeof(a));
            for(ll j=1;j<=m;j++)  
                if(w[j]%i==0){
                    a[x[j]][x[j]]=a[x[j]][x[j]]+fuc(w[j],1);
                    a[y[j]][y[j]]=a[y[j]][y[j]]+fuc(w[j],1);
                    a[x[j]][y[j]]=a[x[j]][y[j]]-fuc(w[j],1);
                    a[y[j]][x[j]]=a[y[j]][x[j]]-fuc(w[j],1);
                }
            (ans+=phi[i]*det().x%P)%=P;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    MFC中小笔记(四)
    MFC中小笔记(三)
    MFC中小笔记
    关于小蜘蛛诞生的坎坎坷坷
    Win32Api程序设计 常用域改变(设定)窗口位置、大小的api
    Win32Api程序设计 注册窗口类
    TCP segment of a reassembled PDU【转】
    计算机网络复习 -- 概念梳理
    指针(pointer) -- (上)
    原来我连真正的调试都不会,每次都是靠编译器(⊙﹏⊙)b
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14302396.html
Copyright © 2011-2022 走看看