CodeChef Weird Product

CodeChef Weird Product

​ 设 pk=i=1kAiXi,且 p0=0。则 1ijN,W(i,j)=pjpi1Xi。于是有

P=i=1Nj=iNW(i,j)2=(i=0Nj=i+1NpjpiXi+1)2=(i=0Nj=i+1N(pjpi))2X2×i=1Ni(Ni)

上式中的分母很好计算,现在主要考虑如何计算分子。

​ 注意到 (pjpi)2=(pjpi)(pipj),则可以考虑将分子变形为

Numerator(P)=(1)N(N+1)2i=0Nji(pjpi)

f(i)=ji(pipj),那么有

Numerator(P)=(1)N(N+1)2i=0N(1)Nf(i)

可以考虑直接将 f(0N1) 求出来。

​ 注意到 f(i) 的结构非常相似,因此我们的想法是找到一个多项式 F(x) 满足 f(i)=F(pi)。如果找到了这样的 F(x),那么就可以对 F(x) 做一遍多项式多点求值来求出 f(i)

​ 于是我们可以考虑类似于拉格朗日插值定理那样的方式去构造:设 F(x)=i=0Nji(xpj),则有等式 F(pi)=ji(pipj)=f(i)。那么又该如何将 F(x) 求出来呢?

​ 让我们先考虑另一个多项式 G(x)=i=0N(xpi)。那么就有 F(x)=i=0NG(x)xpi=G(x)。于是只要将 G(x) 求出来再对其求导就行了。

​ 至于如何求 G(x) 可以用启发式合并+NTT的方法,时间复杂度为 O(Nlog2N);对 F(x) 进行多项式多点求值的时间复杂度也是 O(Nlog2N) 的。总时间复杂度就是线对平方。

参考代码

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

/**
此处省略多项式全家桶的模板
**/

static constexpr value_t mod = poly::P;
inline value_t add(value_t x, value_t y) { return (x += y) >= mod ? x - mod : x; }
inline value_t mul(value_t x, value_t y) { return (int64_t)x * y % mod; }
inline value_t &add_eq(value_t &x, value_t y) { return x = add(x, y); }
inline value_t &mul_eq(value_t &x, value_t y) { return x = mul(x, y); }
inline value_t qpow(value_t x, int64_t y) {
  value_t r = 1;
  for (; y; y >>= 1, mul_eq(x, x))
    (y & 1) && (mul_eq(r, x));
  return r;
} // qpow

static constexpr int Maxn = 1e5 + 5;
int N;
value_t X, iX, Xp, A[Maxn];
value_t p[Maxn], P;
poly getG(int l, int r) {
  if (l == r) return poly{!p[l] ? 0 : mod - p[l], 1};
  int mid = (l + r) >> 1;
  return getG(l, mid) * getG(mid + 1, r);
} // getG
int main(void) {
  int tests;
  scanf("%d", &tests);
  while (tests--) {
    scanf("%d%u", &N, &X);
    p[0] = 0; Xp = 1;
    iX = qpow(X, mod - 2);
    for (int i = 1; i <= N; ++i) {
      scanf("%u", &A[i]);
      p[i] = add(p[i - 1], mul(A[i], mul_eq(Xp, X)));
    }
    auto f = getG(0, N).derivative().evaluate(vector(p, p + N + 1));
    bool neg = (((int64_t)N * (N + 1) / 2) % 2 == 1);
    value_t num = 1;
    for (int i = 0; i <= N; ++i) mul_eq(num, f[i]);
    int64_t Exp = (int64_t)N * (N + 1) * (N + 2) / 3;
    Exp %= (mod - 1);
    value_t dinom = qpow(iX, Exp);
    P = mul_eq(num, dinom);
    printf("%u\n", neg ? (!P ? 0 : mod - P) : P);
  }
  exit(EXIT_SUCCESS);
} // main

posted @   cutx64  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示