[洛谷U22156]未曾届到游览(矩阵树定理)

题目背景


 

又到了某任*堂开关中学一年一度的自主招生考试的时间了,在考试完后许多家长决定带着自己的孩子参观一下这所距千年名校还有890周年的百年学校;

题目描述


 

这所学校的布局非常奇怪,是一个由N 个点M 条边构成的无向图,既然来了肯定要把学校逛完.

家长们的思路也非常清奇,带孩子走过的边刚好有n-1 条,并且这n-1 条边构成了原图的一棵生成树。 每条边上所能看到的风景有两个固定的价值A 和B ,走了这条边就只能得到这条边的A价值,而没走的边就只能得到那条边的B 价值,每个家庭最终看到的总价值的具体算法为所有走过的边的A 值的乘积*所有没选的边的B 值的乘积;

zackzh当年并没有游览学校,所以他想知道所有可能走出的所有生成树的总价值和MOD1e9+7

输入输出格式


 

输入格式:

 

 

11 行两个个整数N 与M ,接下来M 每行四个整数U ,V ,A ,B 描述一条边;

 

输出格式:


 

 

仅一个整数,即可能走出的所有生成树的总价值和MOD1e9+7 ;

 

输入输出样例


 

输入样例#1: 
4 7
1 2 1 1
1 3 1 1
1 4 1 1
1 2 1 2
1 3 1 2
1 4 1 2
1 2 1 2

 

输出样例#1: 

72

 

说明


 

N<=500,M<=200000,A,B<=10;

%10数据满足 N = 3 M = 12

另有%10数据满足N = 12 M = 50

另有%10数据满足N = 500 M = 499

另有%10数据满足N = 500 M = 1000

另有%10数据满足 N = 500 M = 100000

剩下%50 数据满足N = 500 M = 200000

对于%100数据满足1<=A,B<=10

 

分析:


就是考察了一下对矩阵树定理的理解。

在矩阵树定理树一棵生成树的权值求出的其实是它所有边的乘积,

矩阵树定理就求出了所有生成树的权值和。

将边权设为1就可以求出生成树个数。

这道题我们可以把生成树权值设为A / B,设所有边B乘积为ret

那么最终答案就是矩阵树定理求出来的值*ret了。

对于逆元用费马小定理就好了。

AC代码:


 

# include <cstdio>
# include <cstring>
# include <vector>
# include <iostream>
using namespace std;
typedef long long LL;
const LL mod = 1e9 + 7;
void read(int &x)
{
    x = 0;char i = getchar();
    while(!isdigit(i))i = getchar();
    while(isdigit(i))x = x * 10 + i - '0',i = getchar();
}
LL Inc(LL x,LL y){return (x + y + mod) % mod;}
LL Dec(LL x,LL y){return (x - y + mod) % mod;}
LL mul(LL x,LL y){return (x * y % mod + mod) % mod;}
LL a[502][502],ans;int n,m;
LL inv(LL x)
{   LL v = 1;
    for(int i = mod - 2;i;i >>= 1,x = mul(x,x))if(i & 1)v = mul(v,x);
    return v;
}
LL Gauss()
{
    LL ret = 1;
    for(int i = 1;i < n;i++)
    {
        int p = i;
        for(int j = i + 1;j < n;j++)if(a[j][i] > a[p][i])p = j;
        if(p != i)swap(a[p],a[i]),ret = -ret;
        if(!a[i][i])return 0;
        for(int j = i + 1;j < n;j++)
        {
            LL t = a[j][i] * inv(a[i][i]) % mod;
            for(int k = 0;k < n;k++)
            a[j][k] = Dec(a[j][k],mul(t,a[i][k]));
        }
    }
    for(int i = 1;i < n;i++)ret = mul(ret,a[i][i]);
    return ret;
}
int main()
{
    read(n);read(m);int x,y,A,B;LL ret = 1;
    for(int i = 1;i <= m;i++)
    {
        read(x);read(y);read(A);read(B);
        ret = mul(ret,B);
        a[x][y] = Inc(a[x][y],mul(A,inv(B)));
        a[y][x] = Inc(a[y][x],mul(A,inv(B)));
    }
    for(int i = 1;i <= n;i++)
    for(int j = 1;j <= n;j++)
    if(i != j)a[i][i] = Inc(a[i][i],a[i][j]),a[i][j] = mul(-1,a[i][j]);
    printf("%lld\n",mul(ret,Gauss()));
}

 

转载于:https://www.cnblogs.com/lzdhydzzh/p/8648648.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值