[LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞

试题描述

到河北省 见斯大林 / 在月光下 你的背影 / 让我们一起跳舞吧

うそだよ~ 河北省怎么可能有 Stalin。

可是…… 可是如果 Stalin 把自己当作炸弹扔到地堡花园里来了呢?

怀揣着这份小小的希望,元首 Adolf 独自走进了花园。终有一天会重逢的吧,Stalin。或许是在此处,或许是在遥远的彼方。


无论如何,在此之前,好好装点一番花园,编排一段优美的舞步吧!

元首把花园分为 \(n\) 行 \(m\) 列的网格。每个格子中都可以放置一个标识,指向上、下、左、右四个方向中的任意一个。元首位于一个格子时,会按照其中标识所指的方向进入周围的格子,或者走出花园(即目的格子不在网格之内)。举个例子 —— 对于下面的放置方式,元首从第 \(3\) 行第 \(2\) 列的格子开始,会沿着以红色标出的路径走出花园;从第 \(2\) 行第 \(2\) 列的格子开始,则会在以蓝色标出的环路内不断地行走。

元首已经设计好了大部分格子的标识。元首用字符 LRUD 分别表示指向左、右、上、下四个方向的标识,用字符 . 表示未决定的格子。现在,元首希望将每个 . 替换为 LRUD 中任意一种,使得从花园中的任意一个格子出发,按照上述规则行走,都可以最终走出花园。

你需要编写程序帮助元首计算替换的不同方案数。两个方案不同当且仅当存在一个格子,使得两个方案中该格子内的标识不同。当然,由于答案可能很大,只需给出方案数除以 \(10^9 + 7\) 所得的余数即可。

输入

从标准输入读入数据。

输入的第一行包含一个正整数 \(T\) —— 测试数据的组数。接下来包含 \(T\) 组测试数据,格式如下,测试数据间没有空行。

  • 第 \(1\) 行:两个空格分隔的正整数 \(n\)、\(m\) —— 依次表示花园被分成的行数和列数。

  • 接下来 \(n\) 行:每行一个长度为 \(m\) 的由字符 LRUD. 组成的字符串 —— 表示花园内已经确定的格子状态。

输出

输出到标准输出。

对于每组测试数据输出一行 —— 满足条件的方案数除以 \(10^9 + 7\) 所得的余数。

输入示例

5
3 9
LLRRUDUUU
LLR.UDUUU
LLRRUDUUU
4 4
LLRR
L.LL
RR.R
LLRR
4 3
LRD
LUL
DLU
RDL
1 2
LR
2 2
..
..

输出示例

3
8
0
1
192

数据规模及约定

令 \(k\) 表示标记未确定(即包含 “.”)的格子总数。

对于所有数据,有 \(1 \leq T \leq 10\),\(1 \leq n, m \leq 200\),\(0 \leq k \leq \min(nm, 300)\)。

题解

矩阵树定理,有向图有根树的情况。

去掉所有自环,主对角线上第 \(i\) 行第 \(i\) 列是 \(i\) 这个点的出度,剩下的是邻接矩阵取相反数。然后求的是删掉根节点所在行列的余子式的行列式。

注意这个求的是指向根的树形图

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--) int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxr 210
#define maxnode 40010
#define maxn 310
#define MOD 1000000007
#define LL long long int r, c;
char Map[maxr][maxr]; struct Graph {
int n, m, head[maxnode], nxt[maxnode], to[maxnode];
Graph() {}
void clear() {
m = 0; memset(head, 0, sizeof(head));
return ;
}
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
} G; int id(int i, int j) { return (i - 1) * c + j; }
int vis[maxnode];
bool dfs(int u) {
if(vis[u] == 2) return 0;
if(vis[u]) return 1;
vis[u] = 2;
for(int e = G.head[u]; e; e = G.nxt[e]) if(!dfs(G.to[e])) return 0;
vis[u] = 1;
return 1;
} int fa[maxnode];
int findset(int x) { return x == fa[x] ? x : fa[x] = findset(fa[x]); } int n, trid[maxnode];
int ID(int u) { return trid[u] ? trid[u] : trid[u] = ++n; } int A[maxn][maxn], tA[maxn][maxn];
void MatAddEdge(int a, int b) {
if(a == b) return ;
tA[a][a]++; tA[a][b]--;
return ;
}
void elim(int *a, int *b, int tar) {
if(!b[tar]) return ;
int rate = a[tar] / b[tar];
rep(i, 1, n) a[i] = ((LL)a[i] - (LL)b[i] * rate % MOD + MOD) % MOD;
elim(b, a, tar);
return ;
}
int solve() {
n--;
int sgn = 1;
rep(i, 1, n) rep(j, 1, n) if(A[i][j] < 0) A[i][j] += MOD;
rep(i, 1, n)
rep(j, i + 1, n) if(A[j][i]) {
elim(A[i], A[j], i);
if(!A[i][i]) swap(A[i], A[j]), sgn = -sgn;
}
int sum = 1;
rep(i, 1, n) sum = (LL)sum * A[i][i] % MOD;
return (sum * sgn + MOD) % MOD;
} int main() {
int T = read();
while(T--) {
r = read(); c = read(); G.n = r * c + 1; G.clear();
rep(i, 1, G.n) fa[i] = i;
rep(i, 1, r) scanf("%s", Map[i] + 1);
rep(i, 1, r) rep(j, 1, c) if(isalpha(Map[i][j])) {
if(Map[i][j] == 'D') {
G.AddEdge(id(i, j), i < r ? id(i + 1, j) : G.n);
int u = findset(id(i, j)), v = findset(i < r ? id(i + 1, j) : G.n);
if(u != v) fa[v] = u;
}
if(Map[i][j] == 'U') {
G.AddEdge(id(i, j), i > 1 ? id(i - 1, j) : G.n);
int u = findset(id(i, j)), v = findset(i > 1 ? id(i - 1, j) : G.n);
if(u != v) fa[v] = u;
}
if(Map[i][j] == 'R') {
G.AddEdge(id(i, j), j < c ? id(i, j + 1) : G.n);
int u = findset(id(i, j)), v = findset(j < c ? id(i, j + 1) : G.n);
if(u != v) fa[v] = u;
}
if(Map[i][j] == 'L') {
G.AddEdge(id(i, j), j > 1 ? id(i, j - 1) : G.n);
int u = findset(id(i, j)), v = findset(j > 1 ? id(i, j - 1) : G.n);
if(u != v) fa[v] = u;
}
}
memset(vis, 0, sizeof(vis));
bool ok = 1;
rep(i, 1, G.n) if(!vis[i] && !dfs(i)){ puts("0"); ok = 0; break; }
if(!ok) continue; memset(trid, 0, sizeof(trid)); n = 0;
memset(tA, 0, sizeof(tA));
rep(i, 1, r) rep(j, 1, c) if(Map[i][j] == '.') {
int u = findset(id(i, j));
MatAddEdge(ID(u), ID(findset(i < r ? id(i + 1, j) : G.n)));
MatAddEdge(ID(u), ID(findset(i > 1 ? id(i - 1, j) : G.n)));
MatAddEdge(ID(u), ID(findset(j < c ? id(i, j + 1) : G.n)));
MatAddEdge(ID(u), ID(findset(j > 1 ? id(i, j - 1) : G.n)));
} int ci = 0, cj = 0;
rep(i, 1, n) if(i != ID(findset(G.n))) {
ci++;
cj = 0;
rep(j, 1, n) if(j != ID(findset(G.n))) A[ci][++cj] = tA[i][j];
}
printf("%d\n", solve());
} return 0;
}

[LOJ#6259]「CodePlus 2017 12 月赛」白金元首与独舞的更多相关文章

  1. 【LibreOJ】#6259. 「CodePlus 2017 12 月赛」白金元首与独舞

    [题目]给定n行m列的矩阵,每个位置有一个指示方向(上下左右)或没有指示方向(任意选择),要求给未定格(没有指示方向的位置)确定方向,使得从任意一个开始走都可以都出矩阵,求方案数.n,m<=20 ...

  2. 「CodePlus 2017 12 月赛」白金元首与独舞

    description 题面 data range \[ 1 \leq T \leq 10, 1 \leq n, m \leq 200 , 0 \leq k \leq \min(nm, 300)\] ...

  3. 走进矩阵树定理--「CodePlus 2017 12 月赛」白金元首与独舞

    n,m<=200,n*m的方阵,有ULRD表示在这个格子时下一步要走到哪里,有一些待决策的格子用.表示,可以填ULRD任意一个,问有多少种填法使得从每个格子出发都能走出这个方阵,答案取模.保证未 ...

  4. loj6259「CodePlus 2017 12 月赛」白金元首与独舞

    分析 我们将没连的点连向周围四个点 其余的按照给定的方向连 我们将所有连出去的位置统一连到0点上 再以0作为树根 于是就将问题转化为了有向图内向树计数 代码 #include<iostream& ...

  5. loj #6250. 「CodePlus 2017 11 月赛」找爸爸

    #6250. 「CodePlus 2017 11 月赛」找爸爸 题目描述 小 A 最近一直在找自己的爸爸,用什么办法呢,就是 DNA 比对. 小 A 有一套自己的 DNA 序列比较方法,其最终目标是最 ...

  6. [LOJ 6249]「CodePlus 2017 11 月赛」汀博尔

    Description 有 n 棵树,初始时每棵树的高度为 H_i,第 i 棵树每月都会长高 A_i.现在有个木料长度总量为 S 的订单,客户要求每块木料的长度不能小于 L,而且木料必须是整棵树(即不 ...

  7. [LOJ 6248]「CodePlus 2017 11 月赛」晨跑

    Description “无体育,不清华”.“每天锻炼一小时,健康工作五十年,幸福生活一辈子” 在清华,体育运动绝对是同学们生活中不可或缺的一部分.为了响应学校的号召,模范好学生王队长决定坚持晨跑.不 ...

  8. 「CodePlus 2017 12 月赛」火锅盛宴(模拟+树状数组)

    1A,拿来练手的好题 用一个优先队列按煮熟时间从小到大排序,被煮熟了就弹出来. 用n个vector维护每种食物的煮熟时间,显然是有序的. 用树状数组维护每种煮熟食物的数量. 每次操作前把优先队列里煮熟 ...

  9. 「CodePlus 2017 12 月赛」可做题2(矩阵快速幂+exgcd+二分)

    昨天这题死活调不出来结果是一个地方没取模,凉凉. 首先有个一眼就能看出来的规律... 斐波那契数列满足$a_1, a_2, a_1+a_2, a_1+2a_2, 2a_1+3a_2, 3a_1+5a_ ...

随机推荐

  1. 关于var和ES6中的let,const的理解

    var的作用就不多说了,下面说说var的缺点: 1.var可以重复声明 var a = 1; var a = 5; console.log(a); //5 不会报错 在像这些这些严谨的语言来说,一般是 ...

  2. React路由-基础篇

    React-Router-DOM ReactRouter网址, 安装 -npmjs找到react-router-dom -yarn add react-router-dom 基本使用方法 1.创建一个 ...

  3. 最小生成数kruskal算法和prim算法

    定义 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图. 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图. 连通网:在 ...

  4. 笔记-python-standard library-11.2 os.path

    笔记-python-standard library-11.2 os.path 1.      os.path Source code: Lib/posixpath.py (for POSIX), L ...

  5. spark stream简介

    1.复杂的迭代计算 假如我们计算的需要100步的计算,但是当我执行到第99步的时候,突然数据消失, 根据血统,从头进行恢复,代价很高 sc.setCheckpointDir("共享存储文件系 ...

  6. 大话CNN经典模型:VGGNet

       2014年,牛津大学计算机视觉组(Visual Geometry Group)和Google DeepMind公司的研究员一起研发出了新的深度卷积神经网络:VGGNet,并取得了ILSVRC20 ...

  7. Kettle资源库配置(数据库资源库和文件资源库)

    一>文件资源库配置 1. 建立文件资源库:点击工具->资源库->连接资源库菜单 使用文件资源库不需要用户名和密码,如果没有资源库可以点击右上角的"+"新建资源库, ...

  8. Java中的垃圾回收机制&内存管理&内存泄漏

    1. Java在创建对象时,会自动分配内存,并当该对象引用不存在的时候,释放这块内存. 为什么呢? 因为Java中使用被称为垃圾收集器的技术来监视Java程序的运行,当对象不再使用时,就自动释放对象所 ...

  9. Null / Loopback (Null)

    参考: http://www.cnblogs.com/caoguoping100/p/3654452.html https://wiki.wireshark.org/NullLoopback 抓包安装 ...

  10. [转]多多“亦”善:把大量内容放到一页PPT的5个技巧

    技巧一:利用灰色“隐蔽”内容 灰色有个好处:自动成为“备胎”,在“现任”被浏览后才会被注意到.所以使用灰色能够让页面内容看起来没那么多. 技巧二:对齐和亲密 这是排版的两个原则. 对齐是指对页面上的元 ...