CF917D. Stranger Trees & TopCoder13369. TreeDistance(变元矩阵树定理+高斯消元)

题目链接

CF917D:https://codeforces.com/problemset/problem/917/D

TopCoder13369:https://community.topcoder.com/stat?c=problem_statement&pm=13369

题解

首先分析 CF917D。

我们考虑能否将树上的边的贡献特殊表现出来。

记原树为 \(T\),我们构造一幅 \(n\) 个结点的无向完全图,并设置一个值 \(x\),对于无向边 \((u, v)\),其权值 \(w_{(u, v)}\) 满足:

\[w_{(u, v)} = \begin{cases} x, & (u, v) \in T \\ 1, & (u, v) \notin T \end{cases} \]

如果我们令无向完全图的所有生成树中,恰好有 \(i\) 条边属于原树 \(T\) 的生成树数量为 \({\rm ans}_i\),那么不难发现,根据变元矩阵树定理(基尔霍夫矩阵的任意 \(n - 1\) 阶主子式的行列式的绝对值即为所有生成树的边权积之和),求得的基尔霍夫矩阵的 \(n - 1\) 阶主子式的值即为 \(\sum_\limits{i = 0}^{n - 1} x^i {\rm ans}_i\),因为若一个生成树包含了 \(i\) 条原树边,那么该生成树的边权积即为 \(x^i\)

如果我们将 \({\rm ans}_i\) 视为未知数,那么我们就可以通过枚举 \(x: 0 \sim n - 1\) 来得到一个 \(n\) 元线性方程组。直接高斯消元即可求得所有的 \({\rm ans}_i\)

接下来分析 TopCoder13369。

考虑原树 \(T\) 最少能通过多少次操作得到另一棵树 \(T'\),显然若在原树 \(T\) 中出现而在树 \(T'\) 中没有出现的边数为 \(w\),那么只需要 \(w\) 次操作即可将原树 \(T\) 变为树 \(T'\)。因此答案即为有 \(i(i \geq n - 1 - k)\) 条边属于原树的生成树数量,即 \(\sum_\limits{i = n - 1 - k}^{n - 1} {\rm ans}_i\)。其余部分与上题相同。

两题的时间复杂度均为 \(O(n^4)\)

代码

CF917D 代码如下:

#include<bits/stdc++.h>

using namespace std;

const int N = 105, mod = 1e9 + 7;

void add(int& x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
}

void sub(int& x, int y) {
  x -= y;
  if (x < 0) {
    x += mod;
  }
}

int mul(int x, int y) {
  return (long long) x * y % mod;
}

int qpow(int v, int p) {
  int result = 1;
  for (; p; p >>= 1, v = mul(v, v)) {
    if (p & 1) {
      result = mul(result, v);
    }
  }
  return result;
}

int n, a[N][N], b[N][N];
bool old[N][N];

int matrix_tree() {
  int result = 1;
  for (int i = 2; i <= n; ++i) {
    int rev = i;
    for (int j = i + 1; j <= n; ++j) {
      if (a[j][i]) {
        rev = j;
        break;
      }
    }
    if (rev != i) {
      result = (mod - result) % mod;
      for (int j = i; j <= n; ++j) {
        swap(a[i][j], a[rev][j]);
      }
    }
    for (int j = i + 1; j <= n; ++j) {
      int p = mul(a[j][i], qpow(a[i][i], mod - 2));
      for (int k = i; k <= n; ++k) {
        sub(a[j][k], mul(p, a[i][k]));
      }
    }
    result = mul(result, a[i][i]);
  }
  return result;
}

int main() {
  scanf("%d", &n);
  for (int i = 1; i < n; ++i) {
    int u, v;
    scanf("%d%d", &u, &v);
    old[u][v] = old[v][u] = true;
  }
  for (int x = 0; x < n; ++x) {
    memset(a, 0, sizeof a);
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= n; ++j) {
        int v = old[i][j] ? x : 1;
        add(a[j][j], v);
        sub(a[i][j], v);
      }
    }
    for (int i = 0, v = 1; i < n; ++i, v = mul(v, x)) {
      b[x][i] = v;
    }
    b[x][n] = matrix_tree();
  }
  for (int i = 0; i < n; ++i) {
    int rev = i;
    for (int j = i + 1; j < n; ++j) {
      if (b[j][i]) {
        rev = j;
        break;
      }
    }
    if (rev != i) {
      for (int j = i; j <= n; ++j) {
        swap(b[i][j], b[rev][j]);
      }
    }
    for (int j = i + 1; j < n; ++j) {
      int p = mul(b[j][i], qpow(b[i][i], mod - 2));
      for (int k = i; k <= n; ++k) {
        sub(b[j][k], mul(p, b[i][k]));
      }
    }
  }
  for (int i = n - 1; ~i; --i) {
    for (int j = i + 1; j < n; ++j) {
      sub(b[i][n], mul(b[i][j], b[j][n]));
    }
    b[i][n] = mul(b[i][n], qpow(b[i][i], mod - 2));
  }
  for (int i = 0; i < n; ++i) {
    printf("%d%c", b[i][n], " \n"[i == n - 1]);
  }
  return 0;
}

TopCoder13369 代码如下:

直接粘过来改一下即可。

#include<bits/stdc++.h>

using namespace std;

const int N = 55, mod = 1e9 + 7;

void add(int& x, int y) {
  x += y;
  if (x >= mod) {
    x -= mod;
  }
}

void sub(int& x, int y) {
  x -= y;
  if (x < 0) {
    x += mod;
  }
}

int mul(int x, int y) {
  return (long long) x * y % mod;
}

int qpow(int v, int p) {
  int result = 1;
  for (; p; p >>= 1, v = mul(v, v)) {
    if (p & 1) {
      result = mul(result, v);
    }
  }
  return result;
}

int n, a[N][N], b[N][N];
bool old[N][N];

int matrix_tree() {
  int result = 1;
  for (int i = 1; i < n; ++i) {
    int rev = i;
    for (int j = i + 1; j < n; ++j) {
      if (a[j][i]) {
        rev = j;
        break;
      }
    }
    if (rev != i) {
      result = (mod - result) % mod;
      for (int j = i; j < n; ++j) {
        swap(a[i][j], a[rev][j]);
      }
    }
    for (int j = i + 1; j < n; ++j) {
      int p = mul(a[j][i], qpow(a[i][i], mod - 2));
      for (int k = i; k < n; ++k) {
        sub(a[j][k], mul(p, a[i][k]));
      }
    }
    result = mul(result, a[i][i]);
  }
  return result;
}

class TreeDistance {
public:
  int countTrees(vector<int> p, int c) {
    n = p.size() + 1;
    for (int i = 0; i < p.size(); ++i) {
      old[p[i]][i + 1] = old[i + 1][p[i]] = true;
    }
    for (int x = 0; x < n; ++x) {
      memset(a, 0, sizeof a);
      for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
          int v = old[i][j] ? x : 1;
          add(a[j][j], v);
          sub(a[i][j], v);
        }
      }
      for (int i = 0, v = 1; i < n; ++i, v = mul(v, x)) {
        b[x][i] = v;
      }
      b[x][n] = matrix_tree();
    }
    for (int i = 0; i < n; ++i) {
      int rev = i;
      for (int j = i + 1; j < n; ++j) {
        if (b[j][i]) {
          rev = j;
          break;
        }
      }
      if (rev != i) {
        for (int j = i; j <= n; ++j) {
          swap(b[i][j], b[rev][j]);
        }
      }
      for (int j = i + 1; j < n; ++j) {
        int p = mul(b[j][i], qpow(b[i][i], mod - 2));
        for (int k = i; k <= n; ++k) {
          sub(b[j][k], mul(p, b[i][k]));
        }
      }
    }
    for (int i = n - 1; ~i; --i) {
      for (int j = i + 1; j < n; ++j) {
        sub(b[i][n], mul(b[i][j], b[j][n]));
      }
      b[i][n] = mul(b[i][n], qpow(b[i][i], mod - 2));
    }
    int answer = 0;
    for (int i = n - 1; i >= max(0, n - 1 - c); --i) {
      add(answer, b[i][n]);
    }
    return answer;
  }
};
/*
TreeDistance solver;

int main() {
  int n, k;
  scanf("%d", &n);
  vector<int> p(n - 1);
  for (int i = 0; i < n - 1; ++i) {
    scanf("%d", &p[i]);
  }
  scanf("%d", &k);
  printf("%d\n", solver.countTrees(p, k));
  return 0;
}
*/
posted @ 2018-12-21 17:35  ImagineC  阅读(379)  评论(0编辑  收藏  举报