loj3304.「联合省选 2020 A」作业题

题目链接

怎么会有二合一题目放在压轴题qq_emoji: yun

前半部分直接欧拉反演,没啥营养:

\(\sum\limits_T(\sum\limits_{i=1}^{n-1}w_{e_i})\gcd(w_{e_1},\cdots,w_{e_{n-1}})\)

\(=\sum\limits_T(\sum\limits_{i=1}^{n-1}w_{e_i})\sum\limits_{d\mid w_{e_1}\cdots d\mid w_{e_{n-1}}}\varphi(d)\)

\(=\sum\limits_{d=1}^{maxn}\varphi(d)\sum\limits_{T,d\mid w_{e_1},\cdots,d\mid w_{e_{n-1}}}\sum\limits_{i=1}^{n-1}w_{e_i}\)

然后显然是要 \(Matrix-Tree\) 定理,但是普通的矩阵树都是求 \(\prod\limits_Tval_i\),而不能求 \(\sum\limits_Tval_i\)

有一个经典 trick:我们把每条边变成一次多项式 \(w_i+1\) ,然后按照正常的方式求行列式,得到的一次项系数就是答案。这是因为每个乘出一次的部分都相当于钦定了选一条边,其他边任意选的方案。

然后就没了。

#include<iostream>
#include<cstdio>
using namespace std;
#define int long long
const int mod=998244353;
inline int pw(int a,int b)
{
    int res=1;
    while(b)
    {
        if(b&1)
            res=res*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return res;
}
struct edge
{
    int x,y,w;
}e[1001];
struct cp
{
    int x,y;
    cp(int x_=0,int y_=0):
        x(x_),y(y_){}
    cp operator +(const cp &other) const
    {
        return cp((x+other.x)%mod,(y+other.y)%mod);
    }
    cp operator +=(const cp &other)
    {
        x=(x+other.x)%mod;
        y=(y+other.y)%mod;
        return *this;
    }
    cp operator -(const cp &other) const
    {
        return cp((x-other.x+mod)%mod,(y-other.y+mod)%mod);
    }
    cp operator -=(const cp &other)
    {
        x=(x-other.x+mod)%mod;
        y=(y-other.y+mod)%mod;
        return *this;
    }
    cp operator *(const cp &other) const
    {
        return cp((x*other.y%mod+y*other.x%mod)%mod,y*other.y%mod);
    }
    cp operator *=(const cp &other)
    {
        x=(x*other.y%mod+y*other.x%mod)%mod;
        y=y*other.y%mod;
        return *this;
    }
    cp operator /(const cp &other) const
    {
        return cp((x*other.y%mod-y*other.x%mod+mod)%mod*pw(other.y*other.y%mod,mod-2)%mod,y*pw(other.y,mod-2)%mod);
    }
}g[31][31];
int maxn,ans,n,m,phi[200001],cnt,p[200001],sum[200001];
bool prime[200001];
inline int read()
{
    int x=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
}
inline void init()
{
    phi[1]=1;
    for(register int i=2;i<=maxn;++i)
    {
        if(!prime[i])
        {
            p[++cnt]=i;
            phi[i]=i-1;
        }
        for(register int j=1;j<=cnt&&i*p[j]<=maxn;++j)
        {
            prime[i*p[j]]=1;
            if(i%p[j]==0)
            {
                phi[i*p[j]]=p[j]*phi[i];
                break;
            }
            else
                phi[i*p[j]]=(p[j]-1)*phi[i];
        }
    }
}
inline int solve()
{
    cp ans=cp(0,1);
    bool tag=0;
    for(register int i=2;i<=n;++i)
    {
        for(register int j=i+1;j<=n;++j)
            if(!g[i][i].y&&g[j][i].y)
            {
                swap(g[i],g[j]);
                tag^=1;
                break;
            }
        ans*=g[i][i];
        cp d=cp(0,1)/g[i][i];
        for(register int j=i;j<=n;++j)
            g[i][j]*=d;
        for(register int j=i+1;j<=n;++j)
        {
            d=g[j][i];
            for(register int k=i;k<=n;++k)
                g[j][k]-=g[i][k]*d;
        }
    }
    return tag? (mod-ans.x)%mod:ans.x;
}
signed main()
{
    n=read(),m=read();
    for(register int i=1;i<=m;++i)
    {
        e[i].x=read(),e[i].y=read(),maxn=max(e[i].w=read(),maxn);
        for(register int j=1;j*j<=e[i].w;++j)
            if(e[i].w%j==0)
            {
                ++sum[j];
                if(j*j!=e[i].w)
                    ++sum[e[i].w/j];
            }
    }
    init();
    for(register int d=1;d<=maxn;++d)
    {
        if(sum[d]<n-1)
            continue;
        for(register int i=1;i<=n;++i)
            for(register int j=1;j<=n;++j)
                g[i][j]=cp(0,0);
        for(register int i=1;i<=m;++i)
        {
            if(e[i].w%d)
                continue;
            g[e[i].x][e[i].x]+=cp(e[i].w,1);
            g[e[i].y][e[i].y]+=cp(e[i].w,1);
            g[e[i].x][e[i].y]-=cp(e[i].w,1);
            g[e[i].y][e[i].x]-=cp(e[i].w,1);
        }
        ans=(ans+phi[d]*solve()%mod)%mod;
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2021-08-24 22:07  绝顶我为峰  阅读(91)  评论(0编辑  收藏  举报