P6667 [清华集训2016] 如何优雅地求和 题解

一道非常有启发性的题目。

思路#

考虑对于一个给出点值的多项式函数如何处理。

我们发现,对于一个 m 次多项式 f(x),由于 (xi)i 次多项式,所以说我们必定可以把一个多项式函数写成如下模样:

F(k)=i=0m(ki)fi

可以看出,fi 实际上是非常好得到的。

我们可以进行二项式反演。

fk=i=0m(ki)(1)kiF(i)=k!i=0mF(i)i!(1)ki(ki)!

卷积处理即可。

这样的话我们就可以使用简单的组合数快速求出多项式的点值。

感觉这个操作还是很巧妙的,可能还比较通用。

对于这道题,剩下的部分就很简单了,我们可以:

=k=0ni=0m(ki)fi(nk)xk(1x)nk=i=0mfik=0n(ki)(nk)xk(1x)nk=i=0mfik=0nn!k!k!(nk)!i!(ki)!xk(1x)nk=i=0mfin!i!k=0n1(nk)!(ki)!xk(1x)nk=i=0mfin!i!(ni)!k=0n(ni)!(nk)!(ki)!xk(1x)nk=i=0mfi(ni)k=0n(niki)xk(1x)nk=i=0mfi(ni)k=0ni(nik)xk+i(1x)nki=i=0mfixi(ni)k=0ni(nik)xk(1x)nik=i=0mfixi(ni)(x+1x)ni=i=0mfixi(ni)

复杂度瓶颈在前面的处理 fi

时间复杂度:O(mlogm)

Code#

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

const int mod = 998244353;
const int G = 3;
const int I = 332748118;

int n, m, x, k;
int a[20010];
int fc[20010];
int iv[20010];
int f[1 << 16];
int g[1 << 16];
int b[1 << 16];
int w[1 << 16];

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 void init(int n) {
  int x = __lg(n) + 1;
  if (k == (1 << x)) return;
  k = (1 << x);
  for (int i = 0; i < k; i++)
    b[i] = (b[i >> 1] >> 1) | ((i & 1) ? (k >> 1) : 0);
}
inline void ntt(int *f, int n, int flag) {
  init(n), w[0] = 1;
  for (int i = 0; i < k; i++) if (i < b[i]) swap(f[i], f[b[i]]);
  for (int i = 1; i < k; i <<= 1) {
    int b = i << 1;
    int w0 = power((flag ? G : I), (mod - 1) / b);
    for (int j = 1; j < i; j++) w[j] = 1ll * w[j - 1] * w0 % mod;
    for (int j = 0; j < k; j += b) {
      for (int l = 0; l < i; l++) {
        int x = f[j + l], y = 1ll * f[j + l + i] * w[l] % mod;
        f[j + l] = (x + y >= mod ? x + y - mod : x + y);
        f[j + l + i] = (x - y < 0 ? x - y + mod : x - y);
      }
    }
  }
  if (flag == 0) {
    int iv = power(k, mod - 2);
    for (int i = 0; i < k; i++) f[i] = 1ll * f[i] * iv % mod;
  }
}

int main() {
  cin >> n >> m >> x;
  for (int i = 0; i <= m; i++) cin >> a[i];
  fc[0] = 1;
  for (int i = 1; i <= m; i++) fc[i] = 1ll * fc[i - 1] * i % mod;
  iv[m] = power(fc[m], mod - 2);
  for (int i = m; i >= 1; i--) iv[i - 1] = 1ll * iv[i] * i % mod;
  for (int i = 0; i <= m; i++) {
    f[i] = 1ll * a[i] * iv[i] % mod;
    g[i] = (i & 1 ? mod - iv[i] : iv[i]);
  }
  ntt(f, m + m, 1);
  ntt(g, m + m, 1);
  for (int i = 0; i < k; i++)
    f[i] = 1ll * f[i] * g[i] % mod;
  ntt(f, m + m, 0);
  int sm = 1;
  int ns = 0;
  for (int i = 0; i <= m; i++) {
    ns = (ns + 1ll * sm * f[i]) % mod;
    sm = (1ll * sm * x) % mod;
    sm = (1ll * sm * (n - i)) % mod;
  }
  cout << ns << "\n";
}

作者:JiaY19

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

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

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