走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞
n,m<=200,n*m的方阵,有ULRD表示在这个格子时下一步要走到哪里,有一些待决策的格子用.表示,可以填ULRD任意一个,问有多少种填法使得从每个格子出发都能走出这个方阵,答案取模。保证未确定的格子<=300。
。。。一脸懵逼地写了原本30实际20的暴力然后跪着啃了下论文
然而什么都没啃懂不如结论记下来:
首先矩阵行列式的定义:一个n*n的矩阵,行列式值为$\sum_{b是n的一个排列} \ \ \ \ \ ( (-1)^{b的逆序对数} \ \ \ \ \ * \prod_{i=1}^{n} A_{i,b_i})$
矩阵A行列式的性质:
|A|=|A的转置|
|AB|=|A||B|
|A|+|B|=|A和B的某一行加起来其他不变的矩阵|
交换两行得B,|B|=-|A|,因为每次计算一个排列时逆序对数都相差1。
A的某行乘个x得B,|B|=x|A|。
A的某行乘某个数加到另一行上得B,|B|=|A|。由第三条可得。
每行每列和为0的矩阵行列式为0。
由四五六条可用高斯消元计算行列式。
基尔霍夫矩阵:
无向图:矩阵对角线上是度数,其他如果对应边存在就是-1,不然就是0。
有向图:矩阵对角线上是入度,其他如果对应边存在就是-1,不然就是0。
矩阵树定理:一个无向图的基尔霍夫矩阵的任意一个n-1阶的子矩阵,即删掉了第i行和第i列,的行列式是这个图的生成树个数。
一个有向图以点i为根的生成树个数是基尔霍夫矩阵去掉第i行和第i列剩下的矩阵的行列式。
如果图为多图,作如下修改:
当$i \neq j$而有重边时,把邻接矩阵的1改成$i$,$j$间的边数;
当$i = j$而有自环时,自环不可能出现在生成树中,统计度数时记得忽略之。
嗯然后就是这道题。
首先,如果在外界虚拟一个节点,把所有指出去的格子都指向它,把所有确定格子向指向的点连边,可得一个有向图。
然后,待确定的点向四个方向都连边,求这个图以外界点为根的反向的树形图即可。为什么呢,首先确定的格子的边一定会选到,因为每个格子只有一条出边,不然他就和其他点断掉了;其次那些待确定的格子为了形成树,只会在四个方向里选一个。
嗯这样只能拿50分。
可以发现那些已经确定的点是多余的,可以缩掉。也就是给所有待确定格子编号,外界点编号0,然后预处理他往上下左右走能遇到的第一个有编号的格子,朝他们连边即可。
然后就大功告成了。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<stdlib.h>
//#include<queue>
#include<math.h>
//#include<time.h>
//#include<iostream>
using namespace std; int n,m,K,T;
const int mod=1e9+;
#define maxn 311
int pos[maxn][maxn],who[maxn][maxn],place[maxn][]; char mp[maxn][maxn]; int powmod(int a,int b)
{
int ans=;
while (b)
{
if (b&) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b>>=;
}
return ans;
} struct Mat
{
int num[maxn][maxn];
void clear() {memset(num,,sizeof(num));}
}mat;
int gauss()
{
int ans=;
for (int i=;i<=K;i++)
{
int id=i;
for (int j=i+;j<=K;j++) if (fabs(mat.num[j][i])>fabs(mat.num[id][i])) id=j;
if (id!=i)
{
ans=1ll*ans*(mod-)%mod;
for (int j=i;j<=K;j++) {int t=mat.num[i][j]; mat.num[i][j]=mat.num[id][j]; mat.num[id][j]=t;}
}
int tmp=powmod(mat.num[i][i],mod-);
for (int j=i+;j<=K;j++)
for (int k=K;k>=i;k--)
mat.num[j][k]-=mat.num[i][k]*1ll*mat.num[j][i]%mod*tmp%mod,
mat.num[j][k]+=mat.num[j][k]<?mod:;
}
for (int i=;i<=K;i++) ans=1ll*ans*mat.num[i][i]%mod;
return ans;
} int vis[maxn][maxn],Time; bool die;
void dfs(int x,int y)
{
if (mp[x][y]=='.') return;
if (vis[x][y])
{
if (pos[x][y]==-) die=;
return;
}
vis[x][y]=;
int tx=x,ty=y;
if (mp[x][y]=='U') x--;
else if (mp[x][y]=='D') x++;
else if (mp[x][y]=='L') y--;
else if (mp[x][y]=='R') y++;
if (x< || x>n || y< || y>m) pos[tx][ty]=;
else pos[tx][ty]=-,dfs(x,y),pos[tx][ty]=pos[x][y];
}
int check(int x,int y)
{
if (x< || x>n || y< || y>m) return ;
return pos[x][y];
} int main()
{
scanf("%d",&T);
while (T--)
{
scanf("%d%d",&n,&m); K=; Time=; die=;
memset(vis,,sizeof(vis));
memset(pos,,sizeof(pos));
for (int i=;i<=n;i++) scanf("%s",mp[i]+);
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (mp[i][j]=='.') pos[i][j]=++K;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (mp[i][j]!='.') dfs(i,j);
if (die) {puts(""); continue;} for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (mp[i][j]=='.')
{
int nx=i,ny=j,now=pos[i][j];
nx++; place[now][]=check(nx,ny); nx--;
nx--; place[now][]=check(nx,ny); nx++;
ny++; place[now][]=check(nx,ny); ny--;
ny--; place[now][]=check(nx,ny); ny++;
}
mat.clear();
for (int i=;i<=K;i++)
{
for (int j=;j<;j++) mat.num[place[i][j]][i]--;
mat.num[i][i]+=;
}
for (int i=;i<=K;i++)
for (int j=;j<=K;j++)
if (mat.num[i][j]<) mat.num[i][j]+=mod;
printf("%d\n",gauss());
}
return ;
}
走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞的更多相关文章
- [LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞
[LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞 试题描述 到河北省 见斯大林 / 在月光下 你的背影 / 让我们一起跳舞吧 うそだよ~ 河北省怎么可能有 Stalin. ...
- 【LibreOJ】#6259. 「CodePlus 2017 12 月赛」白金元首与独舞
[题目]给定n行m列的矩阵,每个位置有一个指示方向(上下左右)或没有指示方向(任意选择),要求给未定格(没有指示方向的位置)确定方向,使得从任意一个开始走都可以都出矩阵,求方案数.n,m<=20 ...
- 「CodePlus 2017 12 月赛」白金元首与独舞
description 题面 data range \[ 1 \leq T \leq 10, 1 \leq n, m \leq 200 , 0 \leq k \leq \min(nm, 300)\] ...
- loj6259「CodePlus 2017 12 月赛」白金元首与独舞
分析 我们将没连的点连向周围四个点 其余的按照给定的方向连 我们将所有连出去的位置统一连到0点上 再以0作为树根 于是就将问题转化为了有向图内向树计数 代码 #include<iostream& ...
- 「CodePlus 2017 12 月赛」火锅盛宴(模拟+树状数组)
1A,拿来练手的好题 用一个优先队列按煮熟时间从小到大排序,被煮熟了就弹出来. 用n个vector维护每种食物的煮熟时间,显然是有序的. 用树状数组维护每种煮熟食物的数量. 每次操作前把优先队列里煮熟 ...
- 「CodePlus 2017 12 月赛」可做题2(矩阵快速幂+exgcd+二分)
昨天这题死活调不出来结果是一个地方没取模,凉凉. 首先有个一眼就能看出来的规律... 斐波那契数列满足$a_1, a_2, a_1+a_2, a_1+2a_2, 2a_1+3a_2, 3a_1+5a_ ...
- 【LibreOJ】#6257. 「CodePlus 2017 12 月赛」可做题2
[题意]数列满足an=an-1+an-2,n>=3.现在a1=i,a2=[l,r],要求满足ak%p=m的整数a2有多少个.10^18. [算法]数论(扩欧)+矩阵快速幂 [题解]定义fib(i ...
- 【LIbreOJ】#6256. 「CodePlus 2017 12 月赛」可做题1
[题意]定义一个n阶正方形矩阵为“巧妙的”当且仅当:任意选择其中n个不同行列的数字之和相同. 给定n*m的矩阵,T次询问以(x,y)为左上角的k阶矩阵是否巧妙.n,m<=500,T<=10 ...
- 「CodePlus 2017 12 月赛」火锅盛宴
n<=100000种食物,给每个食物煮熟时间,有q<=500000个操作:在某时刻插入某个食物:查询熟食中编号最小的并删除之:查询是否有编号为id的食物,如果有查询是否有编号为id的熟食, ...
随机推荐
- JSR-303原生支持的限制
@Null: 限制只能为null@NotNull: 限制必须不为null@AssertFalse: 限制必须为false@AssertTrue: 限制必须为true@DecimalMax(value) ...
- AJPFX学习Java函数知识总结
函 数:为了提高代码的复用性,可以将其定义成一个单独的功能,该功能的体现就是java中的函数.函数就是体现之一. java中的函数的定义格式: 修饰符 返回值类型 函数名(参数类型 形 ...
- Java多线程——线程的创建方式
Java多线程——线程的创建方式 摘要:本文主要学习了线程的创建方式,线程的常用属性和方法,以及线程的几个基本状态. 部分内容来自以下博客: https://www.cnblogs.com/dolph ...
- CAS介绍
1.概述 单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一.SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统. 耶 ...
- AndroidStudio启动App时,数据取不到。
最近在用AndroidStudio开发App的时候,所连的服务器如果是换成本机上的,那么启动App的时候数据就读取不出来,连其它电脑上的服务器就是正常的,如下: 05-11 09:36:57.178 ...
- 2015年度精品 最新力作32位和64位xp,win7,win8,win10系统下载(电脑城专用版)
一.系统主要特点 1.安装维护方便快速 - 全自动无人值守安装,采用万能GHOST技术,安装系统过程只需3-5分钟,适 合新旧各种机型. - 集成常见硬件驱动,智能识别+预解压技术,绝大多数硬件可以快 ...
- GA详解
转:http://blog.csdn.net/u010451580/article/details/51178225 本文是去年课题组周报中的一个专题讲解,详细讲了GA,由于是周报,所以十分详细.很适 ...
- $("[lay-id='demo'] tbody tr[data-index=0]") 查找某行layui table
$("[lay-id='demo'] tbody tr[data-index=0]")
- 除了上万的月薪之外,还有什么理由让我们必须学Python?
虽然目前的编程语言有很多,但是基础语法上的概念,本质上都是相通的.可以做到一通百通.所以没有必要为了学哪门语言纠结太多. python是目前市面上,我个人认为是最简洁&&最优雅& ...
- jdk11 eclipse下开启ZGC
平台支持 ZGC目前只在Linux/x64上可用,如果有足够的需求,将来可能会增加对其他平台的支持. 对的,目前只支持64位的linux系统. -_-' eclipse.ini配置: -XX:+Unl ...