JZOJ5153:树形图求和
Description
Input
Output
HINT
题解:
一种很直观的想法是通过矩阵生成树求树形图方法数ans以及不包含某一条边i的树形图方法数ans[i],则答案为Σ(ans-ans[i])*w[i]。
对于树形图,矩阵生成树的建立方法是:将有向边(u,v)加入,即inc(A[u,u])(如果是要求n能够走到所有点则inc(A[v,v])),dec(A[u,v])。
删去第n行与第n列后,求行列式(即m[n,n],m为余子式矩阵)。
对于要删去某条边情况下的m[n,n],只要修改矩阵的两项,再求m[n,n]。因为总要删去第n行与第n列,所以只要保留(n-1)*(n-1)的矩阵,每次求整个矩阵的行列式即可。
但是这样做肯定会TLE,考虑使用伴随矩阵去优化。
有公式:
其中,A为原矩阵,A*为A的伴随矩阵,即A的代数余子式矩阵cof A的转置。(cof A[i,j]=(-1)^(i+j)*m[i,j])
我们通过高斯消元求行列式以及矩阵求逆,计算出A*,转置得到cof A。
对矩阵行展开求行列式的公式是:
当删去一条边时,只修改了A[u,u]与A[u,v],它们都在第u行,所以cof A的第u行不变。
我们可以按第u行展开,O(n)求解。多预处理一些东西,甚至可以O(1)求解。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mo=;
int n,m;
int ksm(int xx,int yy)
{
int zz=;
while(yy)
{
if(yy&)zz=(1ll*xx*zz)%mo;
yy>>=; xx=(1ll*xx*xx)%mo;
}
return zz;
}
int t[][];
struct matrix
{
int a[][];
void cheng(matrix &b) //矩阵乘法
{
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
{
t[i][j]=;
for(int k=;k<=n;k++)t[i][j]=(1ll*a[i][k]*b.a[k][j]+t[i][j])%mo;
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)a[i][j]=t[i][j];
}
void hswap(int i,int j) //矩阵行交换
{
for(int k=;k<=*n;k++){ int t=a[i][k]; a[i][k]=a[j][k]; a[j][k]=t; }
}
void hadd(int i,int j,int l) //矩阵行之间加减
{
for(int k=;k<=*n;k++)a[j][k]=(1ll*l*a[i][k]+a[j][k])%mo;
}
void qiuni() //矩阵求逆
{
int flag=;
for(int i=;i<=n;i++)a[i][i+n]=;
for(int i=;i<=n;i++)
{
int j=i; while((j<=n)and(a[j][i]==))j++;
if(j>n){ flag=; break; }
if(i!=j)hswap(i,j);
for(int j=i+;j<=n;j++)if(a[j][i]!=)
{
int xx=(1ll*a[j][i]*ksm(a[i][i],mo-))%mo;
hadd(i,j,(-xx)%mo);
}
}
if(flag==){ for(int i=;i<=n;i++)for(int j=;j<=*n;j++)a[i][j]=; return; }
for(int i=n;i>=;i--)
{
int xx=ksm(a[i][i],mo-); hadd(i,i,(xx-)%mo);
for(int j=;j<i;j++)if(a[j][i]!=)hadd(i,j,(-a[j][i])%mo);
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){ a[i][j]=a[i][j+n]; a[i][j+n]=; }
}
int det() //求矩阵行列式
{
int ans=;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)t[i][j]=a[i][j];
for(int i=;i<=n;i++)
{
int j=i; while((j<=n)and(a[j][i]==))j++;
if(j>n)break;
if(i!=j)hswap(i,j),ans=-ans;
for(int j=i+;j<=n;j++)if(a[j][i]!=)
{
int xx=(1ll*a[j][i]*ksm(a[i][i],mo-))%mo;
hadd(i,j,(-xx)%mo);
}
}
for(int i=;i<=n;i++)ans=(1ll*ans*a[i][i])%mo;
for(int i=;i<=n;i++)for(int j=;j<=n;j++)a[i][j]=t[i][j];
return ans;
}
void zhuanzhi() //矩阵转置
{
for(int i=;i<=n;i++)
for(int j=;j<i;j++){ int t=a[i][j]; a[i][j]=a[j][i]; a[j][i]=t; }
}
} x,y;
int b[][],w[];
int ans,ans2,tot;
int main()
{
freopen("calc.in","r",stdin);
freopen("calc.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++)scanf("%d%d%d",&b[i][],&b[i][],&w[i]);
for(int i=;i<=m;i++)
if(b[i][]<n){ (x.a[b[i][]][b[i][]]+=)%=mo; if(b[i][]<n)(x.a[b[i][]][b[i][]]-=)%=mo; }
n--; int ans=x.det();
int ni=ksm(ans,mo-);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)y.a[i][j]=(1ll*x.a[i][j]*ni)%mo;
y.qiuni(); y.zhuanzhi();
for(int i=;i<=m;i++)
if(b[i][]<=n)
{
ans2=;
(x.a[b[i][]][b[i][]]-=)%=mo; if(b[i][]<=n)(x.a[b[i][]][b[i][]]+=)%=mo;
for(int j=;j<=n;j++)
ans2=(1ll*x.a[b[i][]][j]*y.a[b[i][]][j]+ans2)%mo;
tot=(1ll*(ans-ans2)*w[i]+tot)%mo;
(x.a[b[i][]][b[i][]]+=)%=mo; if(b[i][]<=n)(x.a[b[i][]][b[i][]]-=)%=mo;
}
tot=(tot+mo)%mo;
printf("%d\n",tot);
}
JZOJ5153:树形图求和的更多相关文章
- Java程序:从命令行接收多个数字,求和并输出结果
一.设计思想:由于命令行接收的是字符串类型,因此应先将字符串类型转化为整型或其他字符型,然后利用for循环求和并输出结果 二.程序流程图: 三.源程序代码: //王荣荣 2016/9/23 ...
- Java之递归求和的两张方法
方法一: package com.smbea.demo; public class Student { private int sum = 0; /** * 递归求和 * @param num */ ...
- EXCEL中对1个单元格中多个数字求和
如A1=3779.3759.3769.3781.3750,A2对A1中4个数字求和怎么求!请高手赐教! 方法一:在B1中输入公式=SUM(MID(A1,{1,6,11,16,21},4)*1) 方法二 ...
- codevs 1082 线段树区间求和
codevs 1082 线段树练习3 链接:http://codevs.cn/problem/1082/ sumv是维护求和的线段树,addv是标记这歌节点所在区间还需要加上的值. 我的线段树写法在运 ...
- 从sum()求和引发的思考
sum()求和是一个非常简单的函数,以前我的写法是这样,我想大部分和我一样刚开始学习JS的同学写出来的也会是这样. function sum() { var total=null; for(var i ...
- //给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和
//给定N个整数序列{A1,A2,A3...An},求函数f(i,j)=(k=i~j)Ak的求和 # include<stdio.h> void main() { ,sum1; ]={,- ...
- Ajax中get请求和post请求
我们在使用Ajax向服务器发送数据时,可以采用Get方式请求服务器,也可以使用Post方式请求服务器,那么什么时候该采用Get方式,什么时候该采用Post方式呢? Get请求和Post请求的区别: 1 ...
- bzoj4349: 最小树形图
最小树形图模板题…… 这种\(O(nm)\)的东西真的能考到么…… #include <bits/stdc++.h> #define N 60 #define INF 1000000000 ...
- POJ 2823 Sliding Window 线段树区间求和问题
题目链接 线段树区间求和问题,维护一个最大值一个最小值即可,线段树要用C++交才能过. 注意这道题不是求三个数的最大值最小值,是求k个的. 本题数据量较大,不能用N建树,用n建树. 还有一种做法是单调 ...
随机推荐
- 利用Process类创建多个子进程对象执行任务,主进程负责调度
import time from multiprocessing import Process def run1(): for i in range(5): print("sunck is ...
- mysql查找字段空、不为空的方法总结
1.不为空 Select * From table_name Where id<>'' Select * From table_name Where id!='' 2.为空 Select ...
- Java基础之ArrayList类
一.ArrayList ArrayList继承了AbstractList分别实现了List.RandomAccess(随机访问).Cloneable(可被克隆(复制的意思)). Serializabl ...
- BZOJ 1927: [Sdoi2010]星际竞速(费用流)
传送门 解题思路 仿照最小路径覆盖问题,用费用流解决此题.最小路径覆盖问题是拆点连边后用\(n-\)最大匹配,这里的话也是将每个点拆点,源点向入点连流量为\(1\),费用为\(0\)的边,向出点连流量 ...
- JS互相调用
JS互相调用 例1: <html> <head> <meta charset="UTF-8"> <script type="te ...
- Invoking destroy method 'close' on bean with name 'dataSource'
Invoking destroy method 'close' on bean with name 'dataSource' Spring与Mybatis整合时出现的问题,找了一晚上结果是一个属性写错 ...
- Netty 源码学习——EventLoop
Netty 源码学习--EventLoop 在前面 Netty 源码学习--客户端流程分析中我们已经知道了一个 EventLoop 大概的流程,这一章我们来详细的看一看. NioEventLoopGr ...
- JUC源码分析-集合篇(八)DelayQueue
JUC源码分析-集合篇(八)DelayQueue DelayQueue 是一个支持延时获取元素的无界阻塞队列.队列使用 PriorityQueue 来实现. 队列中的元素必须实现 Delayed 接口 ...
- webstorm vue eslint 自动修正配置
原文:https://medium.com/@netczuk/even-faster-code-formatting-using-eslint-22b80d061461 https://stackov ...
- vue项目使用js-xlsx进行excel表格的导入和导出方法的简单原型封装
前提:已经安装好 file-saver xlsx和 script-loader,如未安装,请查看 https://www.cnblogs.com/luyuefeng/p/8031597.html 新建 ...