zoukankan      html  css  js  c++  java
  • 【洛谷6624】[省选联考 2020 A 卷] 作业题(欧拉函数+矩阵树定理)

    点此看题面

    • 给定一张(n)个点(m)条边的图。
    • 对于图中的所有生成树(T)(假设它的边为(e_{1sim n-1})),求(sum_T(sum_{i=1}^{n-1}w_{e_i} imes gcd(w_{e_1},w_{e_2},...,w_{e_{n-1}})))
    • (nle30,mlefrac{n(n-1)}2,forall wle152501)

    欧拉函数

    看到(gcd)显然一眼想到莫比乌斯反演。

    然而此题其实并不用这么麻烦,我们直接考虑欧拉函数的性质((sum_{d|n}phi(d)=n)),就能更加方便快捷地解决这道题。

    我们枚举(d=gcd(w_{e_1},w_{e_2},...,w_{e_{n-1}}))得到答案式相当于:

    [sum_{d=1}^V phi(d) imessum_{T(d|w_{e_1},d|w_{e_2},...,d|w_{e_{n-1}})}(sum_{i=1}^{n-1}w_{e_i}) ]

    矩阵树定理

    这里给出矩阵树定理的完整版吧:对于一张带权图,按边权建度数矩阵和邻接矩阵,则用矩阵树定理可以求出所有生成树边权之积的和。

    对于这道题自然想到利用矩阵树定理求解,然而定理能求的是边权之积的和,我们要求的是边权之和的和。

    但实际上有一个和与积的经典转化,我们把一条边权为(w)的边看作一个二项式(1+wx),然后发现((1+w_1x)cdot(1+w_2x))的一次项系数恰好是(w_1+w_2)

    所以我们只需要把边权设为二项式即可,具体运算过程中加、减、乘显然都是非常容易的,除的话就考虑((a+bx)cdot(a-bx)=a^2-b^2x^2),由于我们只要二项式,故可以忽略二次项得到((a+bx)cdot(a-bx)equiv a^2(mod x^2)),所以((a+bx)^{-1}=frac{a-bx}{a^2}),就实现了除法向乘法的转化。

    代码:(O(n^3d(n)logn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 30
    #define V 152501
    #define SV 400
    #define X 998244353
    using namespace std;
    int n,m;I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    struct LinearSieve
    {
    	int Pt,P[V+5],phi[V+5];I LinearSieve()//线性筛预处理φ
    	{
    		phi[1]=1;for(RI i=2,j;i<=V;++i) for(!P[i]&&(phi[P[++Pt]=i]=i-1),j=1;j<=Pt&&i*P[j]<=V;++j)
    			if(P[i*P[j]]=1,i%P[j]) phi[i*P[j]]=phi[i]*(P[j]-1);else {phi[i*P[j]]=phi[i]*P[j];break;}
    	}
    }L;
    struct Data
    {
    	int x,y;I Data(CI a=0,CI b=0):x(a),y(b){}
    	I Data operator + (Con Data& o) Con {return Data((x+o.x)%X,(y+o.y)%X);}
    	I Data operator - (Con Data& o) Con {return Data((x-o.x+X)%X,(y-o.y+X)%X);}
    	I Data operator * (Con Data& o) Con {return Data(1LL*x*o.x%X,(1LL*x*o.y+1LL*y*o.x)%X);}
    	I Data operator / (Con Data& o) Con {return *this*Data(o.x,X-o.y)*QP(1LL*o.x*o.x%X,X-2);}//除法转乘法
    }a[N+5][N+5];
    struct line {int x,y,v;I line(CI a=0,CI b=0,CI c=0):x(a),y(b),v(c){}};vector<line> g[V+5];
    I int Calc(CI id)//计算id倍数的生成树边权之和之和
    {
    	if(g[id].empty()) return 0;RI i,j;for(i=1;i<=n;++i) for(j=1;j<=n;++j) a[i][j]=Data();//清空
    	for(vector<line>::iterator it=g[id].begin();it!=g[id].end();++it)//初始化度数矩阵和邻接矩阵
    		a[it->x][it->x]=a[it->x][it->x]+Data(1,it->v),a[it->y][it->y]=a[it->y][it->y]+Data(1,it->v),
    		a[it->x][it->y]=a[it->x][it->y]-Data(1,it->v),a[it->y][it->x]=a[it->y][it->x]-Data(1,it->v);
    	RI k,op=1;Data t;for(i=1;i^n;++i)//求行列式
    	{
    		if(!a[i][i].x&&!a[i][i].y)//如果这一项为空
    		{
    			for(op=X-op,j=i+1;j<=n&&!a[j][i].x&&!a[j][i].y;++j);if(j>n) return 0;//找一行替换
    			for(k=1;k<=n;++k) swap(a[i][k],a[j][k]);//交换
    		}
    		for(j=i+1;j^n;++j) for(t=(Data()-a[j][i])/a[i][i],k=i;k<=n;++k) a[j][k]=a[j][k]+t*a[i][k];//消元
    	}
    	for(t=op,i=1;i^n;++i) t=t*a[i][i];return t.y;//求出对角线元素的乘积
    }
    int main()
    {
    	RI i,j,x,y,z;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) for(scanf("%d%d%d",&x,&y,&z),
    		j=1;j*j<=z;++j) !(z%j)&&(g[j].push_back(line(x,y,z)),j^(z/j)&&(g[z/j].push_back(line(x,y,z)),0));//扔到每个因数对应的vector中
    	RI t=0;for(i=1;i<=V;++i) t=(1LL*L.phi[i]*Calc(i)+t)%X;return printf("%d
    ",t),0;//枚举公约数统计答案
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    非原创-MongoDB PHP 扩展
    原创-docker命令
    原创-k8s nginx内核参数优化
    原创-阿里云K8S-PVCyaml文件挂载云盘
    原创-k8s反亲和性
    使用Virtualenv隔离Python、Ansible不同发行版
    基于Scrapy分布式爬虫的开发与设计
    CentOS7.3中将Python2.7.5 升级到Python3.5.1
    如何让django的model名和应用名显示为中文
    Django添加ckeditor富文本编辑器
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu6624.html
Copyright © 2011-2022 走看看