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建树. 还有一种做法是单调 ...
随机推荐
- ubuntu安装及破解WebStorm11
ubuntu安装及破解WebStorm11 下载地址:http://www.jetbrains.com/webstorm/download/#section=linux-version 1.qingy ...
- grpc协议--客户端构造
由于服务端不在构造,已经构造完成不做构造 gRPC 接口名字为service,proto文件内有定义 1.本目录生成grpc文件 python -m grpc_tools.protoc -I. --p ...
- java中的final关键字的用法
一. 什么是final关键字? final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变 ...
- JavaScript事件绑定的常见方式
在Javascript中,事件绑定一共有3种方式: ① 行内绑定 ② 动态绑定 ③ 事件监听 原文: https://mbd.baidu.com/newspage/data/landingsuper? ...
- Shiro学习(21)授予身份及切换身份
在一些场景中,比如某个领导因为一些原因不能进行登录网站进行一些操作,他想把他网站上的工作委托给他的秘书,但是他不想把帐号/密码告诉他秘书,只是想把工作委托给他:此时和我们可以使用Shiro的RunAs ...
- ubuntu 下gcc的编译运行
一些基本的操作 $gcc test.c //将test.c预处理.汇编.编译并链接形成可执行文件test $gcc test.c -o test //-o用来指定输出文件的文件名 $gcc -E te ...
- DUBBO+Zookeeper在Centos7中本地搭建及小案例
环境: 1.centos7 2.jdk-7u76-linux-x64.tar.gz 2.tomcat:apache-tomcat-7.0.59.tar.gz 3.zookeeper-3.4.6.tar ...
- BZOJ 4516: [Sdoi2016]生成魔咒(后缀数组)
传送门 解题思路 题目其实就是动态维护本质不同的串的个数.考虑到只有加数字的操作,所以可以用后缀数组.题目是每次往后加数字,这样不好处理,因为每次加数字之后所有的后缀都会改变.所以要转化一下思路,就是 ...
- 火狐RESTClient和HttpRequester, Chrome的Postman使用详解
Chrome下有著名的Postman,那火狐也有它的左膀右臂,那就是RESTClient和HttpRequester.这两款工具都是火狐的插件,主要用来模拟发送HTTP请求,HTTP请求最常用的两种方 ...
- Future初次使用理解
当客户端执行方法时,立即返回一个代理对象,此时代理对象没有数据,与此同时开启一个线程去构造真实对象并把真实对象替换掉代理对象(使用set方法).所以就会出现,客户端收到代理对象之后以为执行完了然后执行 ...