【51Nod1446】限制价值树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/82025018

【题目链接】

【思路要点】

  • 首先,也是最重要的一点,我们发现若我们规定一些 g o o d 点为 g r e a t ,剩余的 g o o d 点不为 g r e a t ,可行的生成树的方案仅和我们规定为 g r e a t g o o d 点的数量有关。

  • 因此问题被分成了两个:

    1 计算出 f i 表示我们规定 i g o o d 点为 g r e a t ,可行的生成树的个数。

    2 计算出 g i 表示选取恰好 i g o o d 点,使得它们的权值之和不超过限制的方案数。

    显然有 A n s = i = 0 N f i g i

  • g i 显然可以通过折半搜索 + 二分在 O ( N 2 N 2 ) 的时间内求出。

  • 考虑如何计算 f i ,构建一个 N 个点的完全图,并删去两端都为 g o o d 点并且有至少一端不是被选中的 i 个点中的点的边,用矩阵树定理求解其生成树的个数,记为 h i

    h i 中计算了只有被选中的点与 g o o d 点存在邻边的方案数,应当去掉存在被选中的点只与 b a d 点相连的方案数。

    f i = h i j = 0 i 1 ( i j ) f j

  • 时间复杂度 O ( N 2 N 2 + N 4 )

【代码】


#include<bits/stdc++.h>

using namespace std;
const int MAXN = 45;
const int HALF = 21;
const int MAXS = 1.1e6;
const int INF = 1e9 + 5;
const int P = 1e9 + 7;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
  x = 0; int f = 1;
  char c = getchar();
  for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
  for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
  x *= f;
}
template <typename T> void write(T x) {
  if (x < 0) x = -x, putchar('-');
  if (x > 9) write(x / 10);
  putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
  write(x);
  puts("");
}
struct info {int sum, sel; } half[MAXS];
int n, m, limit, ans, tmp, tot;
int a[MAXN][MAXN], val[MAXN];
int c[MAXN][MAXN], cnt[MAXN];
int pre[MAXS][HALF];
int power(int x, int y) {
  if (y == 0) return 1;
  int tmp = power(x, y / 2);
  if (y % 2 == 0) return 1ll * tmp * tmp % P;
  else return 1ll * tmp * tmp % P * x % P;
}
int gauss(int n) {
  int f = 1;
  for (int i = 1; i <= n; i++) {
      for (int j = i; j <= n; j++)
          if (a[j][i] != 0) {
              if (i != j) f = -f;
              swap(a[i], a[j]);
              break;
          }
      if (a[i][i] == 0) return 0;
      int inv = power(a[i][i], P - 2);
      for (int j = i + 1; j <= n; j++) {
          int tmp = P - a[j][i];
          if (tmp == P) continue;
          tmp = 1ll * tmp * inv % P;
          for (int k = i; k <= n; k++)
              a[j][k] = (a[j][k] + 1ll * a[i][k] * tmp) % P;
      }
  }
  int ans = (P + f) % P;
  for (int i = 1; i <= n; i++)
      ans = 1ll * ans * a[i][i] % P;
  return ans;
}
void addedge(int x, int y) {
  a[x][y] = a[y][x] = P - 1;
  a[x][x]++, a[y][y]++;
}
void work(int pos, int sum, int sel) {
  if (pos == tmp + 1) half[++tot] = (info) {sum, sel};
  else {
      work(pos + 1, sum, sel);
      if (val[pos] != -1 && sum + val[pos] <= limit) work(pos + 1, sum + val[pos], sel + 1);
  }
}
void find(int sum, int sel) {
  int l = 1, r = tot;
  while (l < r) {
      int mid = (l + r + 1) / 2;
      if (half[mid].sum + sum <= limit) l = mid;
      else r = mid - 1;
  }
  for (int i = 0; i <= tmp; i++)
      ans = (ans + 1ll * pre[l][i] * cnt[sel + i]) % P;
}
void getans(int pos, int sum, int sel) {
  if (pos == n + 1) find(sum, sel);
  else {
      getans(pos + 1, sum, sel);
      if (val[pos] != -1 && sum + val[pos] <= limit) getans(pos + 1, sum + val[pos], sel + 1);
  }
}
bool cmp(info a, info b) {
  return a.sum < b.sum;
}
int main() {
  for (int i = 0; i < MAXN; i++)
      c[i][0] = 1;
  for (int i = 1; i < MAXN; i++)
  for (int j = 1; j <= i; j++)
      c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % P;
  int T; read(T);
  while (T--) {
      read(n), read(limit), m = 0;
      for (int i = 1; i <= n; i++) {
          read(val[i]);
          if (val[i] != -1) m++;
      }
      for (int i = 0; i <= m; i++) {
          memset(a, 0, sizeof(a));
          for (int j = 1; j <= n; j++)
          for (int k = j + 1; k <= n; k++)
              if (k > m || k <= i) addedge(j, k);
          cnt[i] = gauss(n - 1);
          for (int j = 0; j < i; j++)
              cnt[i] = (cnt[i] - 1ll * c[i][j] * cnt[j] % P + P) % P;
      }
      ans = 0, tot = 0;
      tmp = n / 2;
      work(1, 0, 0);
      sort(half + 1, half + tot + 1, cmp);
      for (int i = 1; i <= tot; i++) {
          memcpy(pre[i], pre[i - 1], sizeof(pre[i - 1]));
          pre[i][half[i].sel]++;
      }
      getans(tmp + 1, 0, 0);
      writeln(ans);
  }
  return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/82025018
今日推荐