zoukankan      html  css  js  c++  java
  • luoguP6624 [省选联考 2020 A 卷] 作业题(莫比乌斯反演,矩阵树定理)

    luoguP6624 [省选联考 2020 A 卷] 作业题(莫比乌斯反演,矩阵树定理)

    Luogu

    题外话:

    Day2一题没切。

    我是傻逼。

    题解时间

    某种意义上说刻在DNA里的柿子,大概是很多人学莫反做的第一题的套路。

    $ phi cdot 1 = id $ 。

    然后直接转化:

    [egin{aligned} & sum_{T} ( ( sum w_{e_i} ) * gcd( w_{e_i} ) ) \ = & sum_{T} ( ( sum w_{e_i} ) * sum_{d|gcd( w_{e_i} )} phi(d) ) \ = & sum_{d} phi(d) sum_{T:d|gcd( w_{e_i} )} ( sum w_{e_i} ) end{aligned} ]

    然后对于求出边权和,简单的想法是对于每条边求有多少含这条边的树。

    这时就可以想到矩阵树定理了。但怎么对于每个边求呢?

    简单思考发现矩阵元素变成 $ (a+bx) $ 的形式就可以解决。

    一条边加的元素是 $ (1+wx) $ ,答案就是求得结果的一次项系数。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    using namespace std;
    typedef long long lint;
    template<typename TP>inline void read(TP &tar)
    {
    	TP ret=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){ret=ret*10+(ch-'0');ch=getchar();}
    	tar=ret*f;
    }
    namespace RKK
    {
    const int N=40,M=500;
    const int mo=998244353;
    int add(const int &a,const int &b){return a+b>=mo?a+b-mo:a+b;}
    void doadd(int &a,const int &b){if((a+=b)>=mo) a-=mo;}
    int fpow(int a,int p){int ret=1;while(p){if(p&1)ret=1ll*ret*a%mo;a=1ll*a*a%mo,p>>=1;}return ret;}
    int pr[160011],pc,phi[160011];bool npr[160011];
    void sieve()
    {
    	phi[1]=1;for(int i=2;i<=160000;i++)
    	{
    		if(!npr[i]) pr[++pc]=i,phi[i]=i-1;
    		for(int j=1;j<=pc&&i*pr[j]<=160000;j++)
    		{
    			npr[i*pr[j]]=1;
    			if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
    			else phi[i*pr[j]]=phi[i]*(pr[j]-1);
    		}
    	}
    }
    int n,m,ans,ex[M],ey[M],ew[M];
    struct pat
    {
    	int x,y;
    	pat(const int &x=0,const int &y=0):x(x),y(y){}
    	pat operator+(const pat &p)const{return pat(add(x,p.x),add(y,p.y));}
    	pat operator-(const pat &p)const{return pat(add(x,mo-p.x),add(y,mo-p.y));}
    	pat operator*(const pat &p)const{return pat(1ll*x*p.x%mo,(1ll*x*p.y+1ll*p.x*y)%mo);}
    	pat operator/(const pat &p)const
    	{
    		int iv=fpow(p.x,mo-2);
    		return pat(1ll*x*iv%mo,1ll*add(1ll*y*p.x%mo,mo-1ll*x*p.y%mo)*iv%mo*iv%mo);
    	}
    };
    pat a[N][N];
    pat mtree()
    {
    	pat ret=pat(1,0);bool rev=0;
    	for(int l=1,e;l<n;l++)
    	{
    		for(e=l;e<n;e++)if(a[e][l].x) break;if(e==n) return pat(0,0);
    		if(e!=l){rev^=1;for(int j=1;j<n;j++) swap(a[l][j],a[e][j]);}
    		pat k=pat(1,0)/a[l][l];
    		for(int i=l+1;i<n;i++)
    		{
    			pat g=k*a[i][l];
    			for(int j=l;j<n;j++) a[i][j]=a[i][j]-g*a[l][j];
    		}
    		ret=ret*a[l][l];
    	}
    	if(rev) ret=pat(0,0)-ret;return ret;
    }
    int getans(int p)
    {
    	memset(a,0,sizeof(a));
    	for(int i=1;i<=m;i++)if(ew[i]%p==0)
    	{
    		a[ex[i]][ey[i]]=a[ex[i]][ey[i]]-pat(1,ew[i]);
    		a[ey[i]][ex[i]]=a[ey[i]][ex[i]]-pat(1,ew[i]);
    		a[ex[i]][ex[i]]=a[ex[i]][ex[i]]+pat(1,ew[i]);
    		a[ey[i]][ey[i]]=a[ey[i]][ey[i]]+pat(1,ew[i]);
    	}
    	return mtree().y;
    }
    int ct[160011];
    
    
    int main()
    {
    	sieve();read(n),read(m);
    	for(int i=1;i<=m;i++)
    	{
    		read(ex[i]),read(ey[i]),read(ew[i]);
    		for(int j=1;j*j<=ew[i];j++)if(ew[i]%j==0){ct[j]++;if(ew[i]/j!=j) ct[ew[i]/j]++;}
    	}
    	for(int i=1;i<=160000;i++)if(ct[i]>=n-1) ans=(ans+1ll*phi[i]*getans(i))%mo;
    	printf("%d
    ",ans);
    	return 0;
    }
    }
    int main(){return RKK::main();}
    
  • 相关阅读:
    一个完整的Erlang应用
    Erlang的Web库和框架
    erlang lists
    【erlang 网络编程学习】 分析cowboy acceptor实现
    Mochiweb的设计分析
    Misultin, Mochiweb, Cowboy, NodeJS 及 Tornadoweb测评
    用Mochiweb打造百万级Comet应用,第一部分
    欢迎阅读 Erlang OTP 设计原理文档
    erlang app 文件
    转:DataGridView列的宽度、行的高度自动调整
  • 原文地址:https://www.cnblogs.com/rikurika/p/13204284.html
Copyright © 2011-2022 走看看