【学习笔记】[PA 2022] Drzewa rozpinające

这篇具有很好参考价值的文章主要介绍了【学习笔记】[PA 2022] Drzewa rozpinające。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单纯只是为了记录一下这个 trick \text{trick} trick。而且涉及到一点分块矩阵的思想,感觉还挺有意思的。

矩阵行列式引理:设 A A A为可逆矩阵, u , v u,v u,v为列向量,则有: det ( A + u v T ) = det ( A ) ( 1 + v T A − 1 u ) \text{det}(A+uv^T)=\text{det}(A)(1+v^TA^{-1}u) det(A+uvT)=det(A)(1+vTA1u)

非常直白的定理。证明基于一个恒等式: [ I 0 v T 1 ] [ I + u v T u 0 1 ] [ I 0 − v T 1 ] = [ I u 0 1 + v T u ] \begin{bmatrix}I&0\\ v^T&1\end{bmatrix}\begin{bmatrix}I+uv^T&u\\0&1\end{bmatrix}\begin{bmatrix}I&0\\-v^T&1\end{bmatrix}=\begin{bmatrix}I&u\\0&1+v^Tu\end{bmatrix} [IvT01][I+uvT0u1][IvT01]=[I0u1+vTu]。这个不难通过手算验证,顺便说一下,把分块矩阵的每个部分看成一个整体来算就好,这和普通的矩阵乘法没有什么区别。两边同时取行列式,就能得到 det ( I + u v T ) = 1 + v T u \text{det}(I+uv^T)=1+v^Tu det(I+uvT)=1+vTu。那么 L H S = det ( A ( I + u v T A − 1 ) ) = det ( A ) det ( I + u ( v T A − 1 ) ) = det ( A ) ( 1 + v T A − 1 u ) LHS=\text{det}(A(I+uv^TA^{-1}))=\text{det}(A)\text{det}(I+u(v^TA^{-1}))=\text{det}(A)(1+v^TA^{-1}u) LHS=det(A(I+uvTA1))=det(A)det(I+u(vTA1))=det(A)(1+vTA1u),这样就证完了,其实也并不复杂。

值得一提的是,如果 u , v u,v u,v不是向量而是两个矩阵,那么根据分块矩阵的思想上述式子仍然成立,只不过要稍微改动一下: det ( A + u v T ) = det ( A ) det ( I + v T A − 1 u ) \text{det}(A+uv^T)=\text{det}(A)\text{det}(I+v^TA^{-1}u) det(A+uvT)=det(A)det(I+vTA1u)。同样只需要 Laplace \text{Laplace} Laplace展开一下即可。

注意看这个公式把 u , v T u,v^T u,vT的顺序调换了,这样乘出来的矩阵会发生非常大的变化。比如说这道题,非常熟悉的 gcd \text{gcd} gcd矩阵和矩阵树定理,考虑构造 n × m n\times m n×m的矩阵 U , V U,V U,V,其中 U i , d = ϕ ( d ) [ d ∣ a i ] U_{i,d}=\phi(d)[d|a_i] Ui,d=ϕ(d)[dai] V i , d = [ d ∣ a i ] V_{i,d}=[d|a_i] Vi,d=[dai],度数矩阵 D i , i = ∑ gcd ⁡ ( a i , a j ) D_{i,i}=\sum \gcd(a_i,a_j) Di,i=gcd(ai,aj),邻接矩阵 G i , j = gcd ⁡ ( a i , a j ) G_{i,j}=\gcd(a_i,a_j) Gi,j=gcd(ai,aj),那么有 G = U V T G=UV^T G=UVT,根据矩阵树定理我们要求的是 ( n − 1 ) × ( n − 1 ) (n-1)\times (n-1) (n1)×(n1)的代数余子式 det ( D − G ) \text{det}(D-G) det(DG)

注意到 L H S = det ( D − U V T ) = det(D)det ( I m − V T D − 1 U ) LHS=\text{det}(D-UV^T)=\text{det(D)}\text{det}(I_m-V^TD^{-1}U) LHS=det(DUVT)=det(D)det(ImVTD1U),记矩阵 Q = I m − V T D − 1 U Q=I_m-V^TD^{-1}U Q=ImVTD1U,这个矩阵只在 lcm ( i , j ) ≤ m \text{lcm}(i,j)\le m lcm(i,j)m的地方有值,因此是稀疏的,但是这个稀疏是一个很模糊的概念,而且也没有一个固定的算法。本题当中倒着高消会跑的比较快,大概是因为行的编号越大矩阵越稀疏吧。

细节调了半天,还是太菜了。文章来源地址https://www.toymoban.com/news/detail-527157.html

#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=1e9+7;
int n,m,a[5005],phi[5005],cnt,prime[5005],vis[5005],D[5005],invD[5005],gcd[5005][5005];
ll mat[5005][5005],sum[5005],res=1;
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }
    return z;
}
void init(int n){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prime[++cnt]=i,phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else{
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
}
void add(ll &x,ll y){x=(x+y)%mod;}
ll det(){
    int rev=1;
    for(int i=m;i;i--){
        if(!mat[i][i]){
            for(int j=i-1;j;j--){
                if(mat[j][i]){
                    swap(mat[i],mat[j]);
                    rev*=-1;
                    break;
                }
            }
        }
        vector<int>pos;for(int j=i;j;j--)if(mat[i][j])pos.pb(j);
        ll inv=fpow(mat[i][i]);
        for(int j=i-1;j;j--){
            if(mat[j][i]){
                ll tmp=mat[j][i]*inv%mod;
                for(auto p:pos){
                    mat[j][p]=(mat[j][p]-mat[i][p]*tmp)%mod;
                }
            }
            
        }
    }
    ll res=1;for(int i=1;i<=m;i++)res=res*mat[i][i]%mod;
    if(rev==1)return res;
    return mod-res;
}
int getgcd(int x,int y){
    if(x<y)swap(x,y);
    return gcd[x][y];
}
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n;for(int i=1;i<=n;i++)cin>>a[i],m=max(m,a[i]);
    init(m);
    for(int i=0;i<=m;i++){
        for(int j=0;j<=i;j++){
            if(i==0||j==0)gcd[i][j]=i+j;
            else gcd[i][j]=gcd[j][i%j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            D[i]+=getgcd(a[i],a[j]);
        }
    }
    n--;
    for(int i=1;i<=n;i++)invD[i]=fpow(D[i]);
    for(int i=1;i<=n;i++)add(sum[a[i]],invD[i]);
    for(int i=1;i<=m;i++){
        for(int j=i+i;j<=m;j+=i){
            add(sum[i],sum[j]);
        }
    }
    for(int i=1;i<=m;i++)mat[i][i]=1;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            if(i/getgcd(i,j)*j<=m)add(mat[i][j],-phi[j]*sum[i/getgcd(i,j)*j]);
        }
    }
    for(int i=1;i<=n;i++)res=res*D[i]%mod;
    res=res*det()%mod;
    cout<<(res+mod)%mod;
}

到了这里,关于【学习笔记】[PA 2022] Drzewa rozpinające的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 线性代数——高斯消元 学习笔记

    消元法 消元法是将方程组中的一方程的未知数用含有另一未知数的代数式表示,并将其带入到另一方程中,这就消去了一未知数,得到一解;或将方程组中的一方程倍乘某个常数加到另外一方程中去,也可达到消去一未知数的目的。消元法主要用于二元一次方程组的求解。

    2024年02月08日
    浏览(40)
  • 线性代数:克莱姆法则学习笔记

    克莱姆(Cramer)法则又称为克拉默法则,是在线性代数中解决线性方程组问题的一种方法。克莱姆法则的基本思想是通过用系数矩阵的行列式来判断线性方程组是否有唯一解,从而进一步求出各个未知数的值。其原理基于克莱姆定理: 对于 n 元线性方程组 Ax = b,如果系数矩

    2024年02月08日
    浏览(50)
  • 线性代数:约当标准型学习笔记

    线性代数是数学中重要的分支之一,在各个领域中都有广泛的应用。其中,矩阵的基本理论与方法是线性代数的重点和难点。本文主要介绍线性代数中的一种特殊矩阵形式:约当标准型。通过对约当标准型的定义、求法、性质及应用的介绍,希望读者能够深入理解和应用矩阵

    2024年02月04日
    浏览(43)
  • 线性代数 --- 向量的内积(点积)(个人学习笔记)

    向量与向量的乘法 - 内积         两个向量的内积,也叫点积(但在我们这个笔记的前半部分,我们说的,或者用到的更多的应该是点积),他的计算方式是两个同维度向量(例如两个n维向量)的内部元素从1到n, 逐一相乘再相加后的累加和 ,得到的是一个数。 注意,

    2023年04月08日
    浏览(79)
  • 线代学习笔记(一)——线性代数的通俗理解

    本篇笔记内容主要来源于45分钟线性代数通俗讲解_哔哩哔哩_bilibili,非常感谢up主的分享,这里我加入了部分自己的理解,与自己所学的知识结合完成。 数据的维度:即数据含有参数的个数,描述一个对象所需要的参数个数,这样一组数据构成一个多维数据,如一个空间坐标

    2024年02月06日
    浏览(44)
  • 【学习笔记】(数学)线性代数-矩阵的概念和特殊矩阵

    由 m × n mtimes n m × n 个数按一定的次序排成的 m m m 行 n n n 列的矩形数表成为 m × n mtimes n m × n 的矩阵,简称 矩阵 (matrix)。 横的各排称为矩阵的 行 ,竖的各列称为矩阵的 列 。 元素为实数的称为 实矩阵 ,一般情况下我们所讨论的矩阵均为实矩阵。 1 行 n n n 列的矩阵称为

    2024年02月09日
    浏览(45)
  • 线性代数学习笔记4-1:线性方程组的数学和几何意义、零空间/解空间/核

    求解方程 A x ⃗ = v ⃗ mathbf Avec x=vec v A x = v 首先说明系数矩阵的 行数和列数的意义 : 对于系数矩阵 A mathbf A A ,其行数代表方程个数,列数代表未知量个数 对于系数矩阵 A mathbf A A ,矩阵对应线性变换 矩阵 行数 代表变换后的基向量、 x ⃗ vec x x 和 v ⃗ vec v v 等向量的

    2024年02月02日
    浏览(50)
  • 动手学深度学习2.3线性代数-笔记&练习(PyTorch)

    以下内容为结合李沐老师的课程和教材补充的学习笔记,以及对课后练习的一些思考,自留回顾,也供同学之人交流参考。 本节课程地址:线性代数_哔哩哔哩_bilibili 本节教材地址:2.3. 线性代数 — 动手学深度学习 2.0.0 documentation (d2l.ai) 本节开源代码:…d2l-zhpytorchchapter_pr

    2024年04月12日
    浏览(52)
  • 深度学习 精选笔记(1)数据基本操作与线性代数

    学习参考: 动手学深度学习2.0 Deep-Learning-with-TensorFlow-book pytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。 ③非常推荐上面(学习参考)的前两个教程,在网上是开源免费的,写的很棒

    2024年03月10日
    浏览(60)
  • 【AI】《动手学-深度学习-PyTorch版》笔记(五):线性代数

    标量就是我们常见的单个数字(包括整数、小数等等),可以使用只有一个元素的张量表示 用小写字母表示,如:x、y、z

    2024年02月15日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包