测评会员优惠活动进行中 · 开通 VIP,有效期内测评不限次 VIP 优惠中 · 测评不限次 立即查看

A40842. 广场舞

填空题 困难

题目描述

广场舞

题目描述

给定一个 n*m 的格点图,包含 n 行 m 列共 n*m 个顶点,相邻的顶点之间有一条边。

【图1.png】给出了一个3*4的格点图的例子。

如果在图中删除部分顶点和其相邻的边,如上图删除第2行第3列和第3行第1列的顶点后,如【图2.png】所示。

图的生成树指包含图中的所有顶点和其中的一部分边,使得任意两个顶点之间都有由边构成的唯一路径。如果两个生成树包含有不同的边即被认为不同,则上图中共有31种不同的生成树,其中a边不选有10种,a边选有21种。

给出格点图中保留的顶点的信息,请计算该图一共有多少种不同的生成树。

输入格式

输入的第一行包含两个整数n, m,用空格分隔,表示格点图的行数和列数。

接下来n行,每行m个字母(中间没有分隔字符),每个字母必然是大写E或大写N,E表示对应的顶点存在,N表示对应的顶点不存在。保证存在至少一个顶点。

输出格式

输出一行,包含一个整数,表示生成树的个数。答案可能很大,你只需要计算答案除以1000000007的余数即可。

样例输入

3 4

EEEE

EENE

NEEE

样例输出

31

参考答案

#include <bits/stdc++.h> #define mod 1000000007 typedef long long ll; using namespace std; int n, m, cnt, ans, kk; char s[8][100005]; int vis[700000]; int flag[700000]; int fa[700000]; struct E { int u, v; }; E e[1400000]; //路径压缩并查集 int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } //x:边的序号, pcnt:已连接的点的个数, ecnt:已选择的边数 void dfs(int x,int pcnt,int ecnt) { //遍历完所有的边 if(x == cnt) { //点的个数与边的个数符合生成树特点 if(pcnt == n * m - kk && ecnt == n * m - 1 - kk) { for(int i = 0; i <= n * m; i++) { fa[i] = i; } //判断环 for(int i = 0; i < cnt; i++) { if(flag[i] == 1) { int a = find(e[i].u); int b = find(e[i].v); if(a == b) { return ;//如果构成环路 } fa[a]=b; } } ans++; } return ; } int u = e[x].u, v = e[x].v; int k1 = vis[u], k2 = vis[v]; vis[u] = 1; vis[v] = 1; flag[x] = 1; //选择x这条边 //pcnt+2-k1-k2表示要去除以及选择了的点 dfs(x + 1, pcnt + 2 - k1 - k2, ecnt + 1); vis[u] = k1;//注意这里的回溯 vis[v] = k2; flag[x] = 0; //不选择x这条边 dfs(x + 1, pcnt, ecnt); } int main() { cin >> n >> m; for(int i = 1; i <= n; i++) { cin >> s[i]; } for(int i = 1; i <= n; i++) { for(int j = 0; j < m; j++) { //上下方向的边 if(s[i][j] == 'E' && s[i + 1][j] == 'E') { //设置边的端点的序号 e[cnt].u = (i - 1) * m + j + 1; e[cnt++].v = i * m + j + 1; } //左右方向的边 if(s[i][j] == 'E' && s[i][j + 1] == 'E') { e[cnt].u = (i - 1) * m + j + 1; e[cnt++].v = (i - 1) * m + j + 2; } if(s[i][j] == 'N') { kk++; //统计为空的点 } } } dfs(0, 0, 0); cout << ans; return 0; }
上一题 下一题