HDU 6056 simple counting problem

链接:

link

题解:

一种暴力的想法是容斥,那么答案为 ∑ S ( − 1 ) ∣ S ∣ ( n − ∑ j ∈ S ( b j − c + 1 ) + m − 1 m ) \sum_{S} (-1)^{|S|}\binom{n-\sum_{j\in S}(b^j-c+1)+m-1}{m} S(1)S(mnjS(bjc+1)+m1)

f ( x ) = ( x k ) f(x) = \binom{x}{k} f(x)=(kx)

将它变成 k k k次多项式,那么由斯特林数容斥得 f ( x ) = ∑ i = 0 k ( − 1 ) k − i [ k i ] x i k ! f(x) = \frac{\sum_{i=0}^k (-1)^{k-i}{k\brack i} x^i}{k!} f(x)=k!i=0k(1)ki[ik]xi

c c c的限制不太好算,直接暴力枚举 ∣ S ∣ |S| S就可以去掉 c c c的贡献了,那么只需要对于每个 i i i ( ∑ j ∈ S b j ) i (\sum_{j\in S}b^j)^i (jSbj)i

预处理 f ( i , j , k ) f(i, j, k) f(i,j,k)表示考虑 b 1 , b 2 , ⋯   , b i b^1, b^2, \cdots, b^i b1,b2,,bi,选出 j j j个数和的 k k k次方之和,那么由二项式定理 f ( i , j , k ) = f ( i − 1 , j , k ) + ∑ l = 0 k f ( i − 1 , j − 1 , l ) b i ( k − l ) ( k l ) f(i,j,k) = f(i-1,j,k) + \sum_{l=0}^k f(i-1,j-1,l) b^{i(k-l)} \binom{k}{l} f(i,j,k)=f(i1,j,k)+l=0kf(i1,j1,l)bi(kl)(lk)

接下来枚举脱离限制的最高位,之后的数可以任意选,用预处理的 f f f来计算。

代码:

#include <bits/stdc++.h>

using namespace std;

const int md = 998244353;

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

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

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

int main() {
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, b, c;
  string str;
  int qq = 0;
  while (cin >> n >> b >> c >> str) {
    vector<vector<int>> binom(n + 1, vector<int>(n + 1));
    for (int i = 0; i <= n; ++i) {
      binom[i][0] = 1;
      for (int j = 1; j <= i; ++j) {
        binom[i][j] = binom[i - 1][j];
        add(binom[i][j], binom[i - 1][j - 1]);
      }
    }
    vector<vector<int>> stirling(n + 1, vector<int>(n + 1));
    stirling[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
      for (int j = 1; j <= i; ++j) {
        stirling[i][j] = stirling[i - 1][j - 1];
        add(stirling[i][j], mul(stirling[i - 1][j], i - 1));
      }
    }
    vector<int> inv_fact(n + 1);
    inv_fact[0] = inv_fact[1] = 1;
    for (int i = 2; i <= n; ++i) {
      inv_fact[i] = mul(md - md / i, inv_fact[md % i]);
    }
    for (int i = 2; i <= n; ++i) {
      inv_fact[i] = mul(inv_fact[i], inv_fact[i - 1]);
    }
    vector<int> digits(str.size());
    for (int i = 0; i < (int) str.size(); ++i) {
      digits[i] = str[i] - '0';
    }
    reverse(digits.begin(), digits.end());
    vector<int> mod_digits;
    while (!digits.empty()) {
      long long remainder = 0;
      for (int i = (int) digits.size() - 1; ~i; --i) {
        remainder = remainder * 10 + digits[i];
        digits[i] = remainder / b;
        remainder %= b;
      }
      while (!digits.empty() && !digits.back()) {
        digits.pop_back();
      }
      mod_digits.push_back(remainder);
    }
    int value = 0;
    for (int i = mod_digits.size() - 1; ~i; --i) {
      value = mul(value, b);
      add(value, mod_digits[i]);
    }
    add(value, n - 1);
    vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(n + 1, vector<int>(n + 1)));
    dp[0][0][0] = 1;
    for (int i = 1, w = b; i <= n; ++i, w = mul(w, b)) {
      vector<int> power(n + 1);
      power[0] = 1;
      for (int i = 0; i < n; ++i) {
        power[i + 1] = mul(power[i], w);
      }
      dp[i] = dp[i - 1];
      for (int j = 1; j <= i; ++j) {
        for (int k = 0; k <= n; ++k) {
          for (int l = 0; l <= k; ++l) {
            add(dp[i][j][k], mul(mul(binom[k][l], power[k - l]), dp[i - 1][j - 1][l]));
          }
        }
      }
    }
    int ans = 0;
    for (int take = 0; take <= n; ++take) {
      vector<int> power_value(n + 1);
      power_value[0] = 1;
      for (int i = 0; i < n; ++i) {
        power_value[i + 1] = mul(power_value[i], value);
      }
      vector<int> power_b(mod_digits.size());
      power_b[0] = 1;
      for (int i = 1; i < (int) mod_digits.size(); ++i) {
        power_b[i] = mul(power_b[i - 1], b);
      }
      vector<int> coef(n + 1);
      for (int i = 0; i <= n; ++i) {
        for (int j = i; j <= n; ++j) {
          int ways = mul(stirling[n][j], mul(binom[j][i], mul(power_value[j - i], inv_fact[n])));
          if ((i + j + n + take) % 2 == 1) {
            sub(coef[i], ways);
          } else {
            add(coef[i], ways);
          }
        }
      }
      int sum = 0;
      int cnt = 0;
      for (int i = (int) mod_digits.size() - 1; ~i; --i) {
        if (mod_digits[i]) {
          int lim = min(n, max(i - 1, 0));
          vector<int> power_sum(n + 1);
          power_sum[0] = 1;
          for (int j = 0; j < n; ++j) {
            power_sum[j + 1] = mul(power_sum[j], sum);
          }
          for (int j = 0; j <= n; ++j) {
            int ways = 0;
            for (int k = 0; k <= j; ++k) {
              add(ways, mul(binom[j][k], mul(dp[lim][take - cnt][k], power_sum[j - k])));
            }
            add(ans, mul(ways, coef[j]));
          }
          if (cnt < take && i <= n && i) {
            add(sum, power_b[i]);
            ++cnt;
            if (mod_digits[i] > 1) {
              power_sum[0] = 1;
              for (int j = 0; j < n; ++j) {
                power_sum[j + 1] = mul(power_sum[j], sum);
              }
              for (int j = 0; j <= n; ++j) {
                int ways = 0;
                for (int k = 0; k <= j; ++k) {
                  add(ways, mul(binom[j][k], mul(dp[lim][take - cnt][k], power_sum[j - k])));
                }
                add(ans, mul(ways, coef[j]));
              }
              break;
            }
          } else {
            break;
          }
        }
      }
      mod_digits[0] += c - 1;
      if (c > 1) {
        for (int i = 0; i + 1 < (int) mod_digits.size(); ++i) {
          if (mod_digits[i] >= b) {
            mod_digits[i] -= b;
            mod_digits[i + 1]++;
          }
        }
        if (mod_digits.back() >= b) {
          mod_digits.back() -= b;
          mod_digits.push_back(1);
        }
      } else {
        for (int i = 0; i + 1 < (int) mod_digits.size(); ++i) {
          if (mod_digits[i] < 0) {
            mod_digits[i] += b;
            mod_digits[i + 1]--;
          }
        }
        if (mod_digits.back() < 0) {
          break;
        }
      }
      value = ((value + c - 1) % md + md) % md;
    }
    cout << "Case #" << ++qq << ": " << ans << "\n";
  }
  return 0;
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值