P7531 [USACO21OPEN] Routing Schemes P 题解

best 定理居然还有运用范围。

思路#

考虑如何来判断是否有解。

由于每一条边都需要用到。

但是它是使用很多条路径进行覆盖。

我们考虑一个很巧妙的转化。

建立一个超级源点,源点向每一条路径的开头连一条边。每一条路径的结尾向源点连一条边,这样一条路径就变成了一个回路。

把所有回路连起来,就是一条欧拉回路。

问题转化成了欧拉回路计数。

对于欧拉回路计数,我们有经典的 best 定理。

它描述的是这样一个东西。

ans=×(degi1)!

内向树个数容易使用矩阵树定理求出。

考虑这个题实际求的是匹配路径方案数。

所以要把源点的 (degi1)! 除掉。

时间复杂度:O(Tn3)

Code#

#include <bits/stdc++.h>
using namespace std;

const int mod = 1e9 + 7;

int n, k, ru[110], cu[110];
int f[110];
int a[110][110];

inline int power(int x, int y) {
  int res = 1;
  while (y) {
    if (y & 1) res = 1ll * res * x % mod;
    x = 1ll * x * x % mod, y >>= 1;
  }
  return res;
}
inline int gauss() {
  int res = 1;
  for (int i = 1; i <= n; i++)
    for (int j = 1; j <= n; j++)
      if (a[i][j] < 0) a[i][j] += mod;
  for (int i = 1; i <= n; i++) {
    if (ru[i] == 0 && cu[i] == 0) continue;
    int id = i;
    for (int j = i; j <= n; j++) if (a[j][i]) id = j;
    if (id != i) {
      swap(a[i], a[id]), res = mod - res;
    }
    int p = power(a[i][i], mod - 2);
    for (int j = i + 1; j <= n; j++) {
      int c = 1ll * a[j][i] * p % mod;
      for (int k = i; k <= n; k++) {
        a[j][k] = (a[j][k] - 1ll * a[i][k] * c) % mod;
        if (a[j][k] < 0) a[j][k] += mod;
      }
    }
  }
  for (int i = 1; i <= n; i++)
    if (ru[i] || cu[i]) res = 1ll * res * a[i][i] % mod;
  return res;
}
inline void solve() {
  cin >> n >> k;
  for (int i = 1; i <= n; i++) {
    char c;
    cin >> c;
    if (c == 'S') a[0][i]--, a[0][0]++, cu[0]++, ru[i]++;
    if (c == 'R') a[i][0]--, a[i][i]++, cu[i]++, ru[0]++;
  }
  for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= n; j++) {
      char c;
      cin >> c;
      if (c == '1') {
        a[i][j]--;
        a[i][i]++;
        cu[i]++, ru[j]++;
      }
    }
  }
  bool flag = 0;
  for (int i = 0; i <= n; i++)
    if (ru[i] != cu[i]) flag = 1;
  if (flag == 0) {
    int ns = gauss();
    for (int i = 1; i <= n; i++)
      if (cu[i]) ns = 1ll * ns * f[cu[i] - 1] % mod;
    cout << ns << "\n";
  } else {
    cout << 0 << "\n";
  }
  memset(a, 0, sizeof a);
  memset(ru, 0, sizeof ru);
  memset(cu, 0, sizeof cu);
}

int main() {
  int t;
  cin >> t;
  f[0] = 1;
  for (int i = 1; i <= 100; i++) f[i] = 1ll * f[i - 1] * i % mod;
  while (t--) solve();
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/18615392

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示