@description@

给定 N 个整数 a1,a2,...,aN,定义函数 f 和 g:

f(x,k) = (x + a1)^k + (x + a2)^k +···+ (x + aN)^k

g(t,k) = f(0,k) + f(1,k) +···+ f(t,k)

给定整数 T 和 K,对于每个 0 ∼ K 之间的 i,请计算 g(T,i) 对 10^9 + 7 取模的结果。

输入格式

输入的第一行包含三个整数 N,K,T。第二行包含 N 个整数 a1,a2,...,aN。

输出格式

输出一行,包含 K + 1 个整数,代表 g(T,0),g(T,1),...,g(T,K) 对 10^9 + 7 取模的结果。

数据范围与子任务

• 1 ≤ N ≤ 10^5 • 1 ≤ K ≤ 5·10^4

• 1 ≤ T ≤ 10^18 • 0 ≤ ai < 10^9 + 7

样例输入

2 3 4

0 1

样例输出

10 25 85 325

@solution@

@part - 1@

先写式子:

\[g(T, k) = \sum_{i=0}^{T}f(i, k) = \sum_{i=0}^{T}\sum_{j=1}^{N}(i + a_j)^k = \sum_{i=0}^{T}\sum_{j=1}^{N}\sum_{p=0}^{k}C_k^p*i^p*a_j^{k-p}
\]

通过将组合数拆成阶乘形式,并作适当地变形,可以得到:

\[g(T, k) = k!*\sum_{p=0}^{k}((\frac{\sum_{i=0}^{T}i^p}{p!})*(\frac{\sum_{j=1}^{N}a_j^{k-p}}{(k-p)!}))
\]

令 \(P(x) = \frac{\sum_{i=0}^{T}i^x}{x!}, Q(x) = \frac{\sum_{i=1}^{N}a_i^x}{x!}\),则有 \(g(T, k) = k!*\sum_{p=0}^{k}P(p)*Q(k-p)\)。

显然是一个卷积,可以使用 fft(但是这里因为模数的原因,要使用神奇的 mtt)。

我们接下来的问题转为求解 P 和 Q。

@part - 2@

先考虑 P,发现它是一个很裸的自然数幂和,可以使用伯努利数求解。

所以以下介绍使用伯努利数求解自然数幂和的过程,熟悉的人可以直接跳过。

虽然以前写过一点,不过感觉不大详细。

记 \(S(n, k) = \sum_{i=0}^{n}i^k\)。先考虑一种使用裂项相消求解自然数幂和的方法:

首先由二项式定理得到 \((n+1)^{k+1} - n^{k+1} = \sum_{i=0}^{k}C_{k+1}^{i}*n^i\)。

于是:

\[\sum_{i=0}^{n}((i+1)^{k+1} - i^{k+1}) = \sum_{i=0}^{n}\sum_{j=0}^{k}C_{k+1}^{j}*i^j
\]

\[(n+1)^{k+1} = \sum_{j=0}^{k}C_{k+1}^{j}*S(n, j)
\]

\[\frac{(n+1)^{k+1}}{(k+1)!} = \sum_{j=0}^{k}\frac{S(n, j)}{j!}*\frac{1}{(k+1-j)!}
\]

这个式子感觉非常卷积,而且感觉非常指数型生成函数,所以我们就往这个方向思考。

令 \(F_n(x) = \sum_{i=0}\frac{S(n, k)}{k!}x^k\),即 S(n, k) 的指数型生成函数。

则有:\(e^{(n+1)x} - 1 = (e^x - 1)*F_n\),于是就有 \(F_n = \frac{e^{(n+1)x} - 1}{e^x - 1}\)。

因为 \(e^x - 1\) 的常数项为 0,使用牛顿迭代找不出逆元,所以我们可以找 \(\frac{e^x - 1}{x}\) 的逆元(即伯努利数的生成函数)。

于是:

\[F_n = \frac{e^{(n+1)x} - 1}{x}*\frac{x}{e^x - 1} = (\sum_{i=0}\frac{(n+1)^{i+1}}{(i+1)!}*x^i)*(\frac{1}{\sum_{i=0}\frac{1}{(i+1)!}*x^i})
\]

写一个多项式求逆即可。一样,因为模数原因要使用 mtt。

@part - 3@

考虑怎么求解 Q。这应该算是本题最关键的部分。

我们令 R(x) 表示从序列 a 中不重复地选出 x 个数相乘的结果之和。或者说,R 是多项式 \(\prod_{i=1}^{N}(a_i + 1)\) 的系数。

可以使用分治 fft 简单地求出 R。

通过容斥可以求出 Q(x) 的递推式:

\[Q(x) = \sum_{i=1}^{x-1}(-1)^{i-1}*Q(x-i)*R(i) + (-1)^{x-1}*R(x)*x
\]

怎么理解呢?

首先:

\[Q(x-1)*R(1) = \sum_{i=1}^{N}a_i^x + \sum_{i=1}^{N}\sum_{j=1,i\not =j}^{N}a_i^{x-1}*a_j
\]

前一半是我们需要的,后一半是我们想要容斥掉的。

然后:

\[Q(x-2)*R(2) = \sum_{i=1}^{N}\sum_{j=1,i\not =j}^{N}a_i^{x-1}*a_j + \sum_{i=1}^{N}\sum_{j=1,i\not =j}^{N}\sum_{k=1,i\not =k,j\not =k}^{N}a_i^{x-2}*a_j*a_k
\]

同上面一样,前一半是我们想要的,后一半是我们想要容斥掉的。

于是容斥到最后,所有 ai 的指数都为 1 时(不难发现此时项数为 x),可以发现一组 (ai, aj, ak, ...) 的贡献被重复计算了 x 次(在每一项都被计算了一次)。

于是在最后消掉这些贡献即可,即 \((-1)^{x-1}*R(x)*x\) 一项的含义。

怎么想出来的呢?我怎么知道,这是人类的智慧。

然后就是套路一般的生成函数了。

令 \(G(x) = \sum_{i=1}(-1)^{i-1}*R(i)*x^i, H(x) = \sum_{i=1}(-1)^{i-1}*R(i)*i*x^i\)。

则 \(Q = Q*G + H\),于是 \(Q = \frac{H}{1-G}\)。

要考虑 Q 的边界情况,即 \(a_1^0 + a_2^0 + ... + a_N^0\) 的情况。

@accepted code@

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
typedef long double ld;
struct complex{
ld r, i;
complex(ld _r=0, ld _i=0):r(_r), i(_i) {}
friend complex operator + (complex a, complex b) {
return complex(a.r + b.r, a.i + b.i);
}
friend complex operator - (complex a, complex b) {
return complex(a.r - b.r, a.i - b.i);
}
friend complex operator * (complex a, complex b) {
return complex(a.r*b.r - a.i*b.i, a.i*b.r + b.i*a.r);
}
friend complex operator / (complex a, ld k) {
return complex(a.r / k, a.i / k);
}
friend complex conj(complex a) {
return complex(a.r, -a.i);
}
};
typedef complex comp;
const int MAXN = 200000;
const int MOD = int(1E9) + 7;
const int SQ = sqrt(MOD);
const ld PI = acos(-1);
int pow_mod(int b, int p) {
int ret = 1;
while( p ) {
if( p & 1 ) ret = 1LL*ret*b%MOD;
b = 1LL*b*b%MOD;
p >>= 1;
}
return ret;
}
struct poly{
comp w[20];
poly() {
for(int i=0;i<20;i++)
w[i] = comp(cos(2*PI/(1<<i)), sin(2*PI/(1<<i)));
}
void clear(comp *A, int l, int r) {
for(int i=l;i<r;i++)
A[i] = comp(0, 0);
}
void copy(comp *A, comp *B, int n) {
for(int i=0;i<n;i++)
A[i] = B[i];
}
void debug(comp *A, int n) {
for(int i=0;i<n;i++)
cout << A[i].r << " " << A[i].i << endl;
puts("");
}
void debug(int *A, int n) {
for(int i=0;i<n;i++)
cout << A[i] << endl;
puts("");
}
int length(int n) {
int len; for(len = 1; len < n; len <<= 1) ;
return len;
}
void fft(comp *A, int n, int type) {
for(int i=0,j=0;i<n;i++) {
if( i < j ) swap(A[i], A[j]);
for(int k=(n>>1);(j^=k)<k;k>>=1);
}
for(int i=1;(1<<i)<=n;i++) {
int s = (1<<i), t = (s>>1);
comp u = comp(w[i].r, type*w[i].i);
for(int j=0;j<n;j+=s) {
comp p = complex(1, 0);
for(int k=0;k<t;k++,p=p*u) {
comp x = A[j+k], y = p*A[j+k+t];
A[j+k] = x + y, A[j+k+t] = x - y;
}
}
}
if( type == -1 ) {
for(int i=0;i<n;i++)
A[i] = A[i] / n;
}
}
comp tmp2[MAXN + 5], tmp3[MAXN + 5];
void poly_mul(int *A, int *B, int *C, int n, int m, int k) {
int len = length(n + m - 1);
clear(tmp2, 0, len), clear(tmp3, 0, len);
for(int i=0;i<n;i++) tmp2[i] = comp(A[i]/SQ, A[i]%SQ);
for(int i=0;i<m;i++) tmp3[i] = comp(B[i]/SQ, B[i]%SQ);
fft(tmp2, len, 1), fft(tmp3, len, 1);
for(int i=0;i<=len/2;i++) {
comp x0 = tmp2[i], y0 = tmp2[(len-i)%len];
comp x1 = tmp3[i], y1 = tmp3[(len-i)%len];
comp a0 = (x0 + conj(y0))/2, a1 = (conj(y0) - x0)/2*comp(0, 1);
comp b0 = (x1 + conj(y1))/2, b1 = (conj(y1) - x1)/2*comp(0, 1);
tmp2[i] = a0*b0 + comp(0, 1)*a1*b1, tmp3[i] = a0*b1 + a1*b0;
a0 = (y0 + conj(x0))/2, a1 = (conj(x0) - y0)/2*comp(0, 1);
b0 = (y1 + conj(x1))/2, b1 = (conj(x1) - y1)/2*comp(0, 1);
tmp2[(len-i)%len] = a0*b0 + comp(0, 1)*a1*b1, tmp3[(len-i)%len] = a0*b1 + a1*b0;
}
fft(tmp2, len, -1), fft(tmp3, len, -1);
for(int i=0;i<len;i++)
C[i] = (ll(tmp2[i].r+0.5)%MOD*SQ%MOD*SQ%MOD + ll(tmp3[i].r+0.5)%MOD*SQ%MOD + ll(tmp2[i].i+0.5)%MOD)%MOD;
}
int tmp1[MAXN + 5];
void poly_inv(int *A, int *B, int n) {
if( n == 1 ) {
B[0] = pow_mod(A[0], MOD - 2);
return ;
}
poly_inv(A, B, (n + 1) >> 1);
poly_mul(A, B, tmp1, n, (n+1)>>1, n);
for(int i=0;i<n;i++)
tmp1[i] = (MOD - tmp1[i])%MOD;
tmp1[0] = (tmp1[0] + 2)%MOD;
poly_mul(tmp1, B, B, n, (n+1)>>1, n);
}
}oper;
int fct[MAXN + 5], ifct[MAXN + 5];
void init() {
fct[0] = 1;
for(int i=1;i<=MAXN;i++)
fct[i] = 1LL*fct[i-1]*i%MOD;
ifct[MAXN] = pow_mod(fct[MAXN], MOD-2);
for(int i=MAXN-1;i>=0;i--)
ifct[i] = 1LL*ifct[i+1]*(i+1)%MOD;
}
int a[MAXN + 5], N, K; ll T;
int f[MAXN + 5], B[MAXN + 5], tmp[MAXN + 5];
void func1() {
for(int i=0;i<K;i++)
tmp[i] = ifct[i+1];
oper.poly_inv(tmp, B, K);
for(int i=0,p=T;i<K;i++,p=1LL*p*T%MOD)
tmp[i] = 1LL*p*ifct[i+1]%MOD;
oper.poly_mul(tmp, B, f, K, K, K);
}
int g[MAXN + 5], h[MAXN + 5], hl[20][MAXN + 5], hr[20][MAXN + 5];
void func3(int le, int ri, int dep, int *A) {
if( le + 1 == ri ) {
A[0] = 1, A[1] = a[le];
return ;
}
int mid = (le + ri) >> 1;
func3(le, mid, dep + 1, &hl[dep][le]);
func3(mid, ri, dep + 1, &hr[dep][le]);
oper.poly_mul(&hl[dep][le], &hr[dep][le], A, mid-le+1, ri-mid+1, ri-le+1);
}
void func2() {
func3(0, N, 0, h);
for(int i=0,f=1;i<K;i++,f=1LL*f*(MOD-1)%MOD)
h[i] = 1LL*f*h[i]%MOD;
oper.poly_inv(h, g, K);
for(int i=0;i<K;i++)
h[i] = 1LL*(MOD-1)*h[i]%MOD*i%MOD;
oper.poly_mul(h, g, g, K, K, K);
g[0] = N;
for(int i=0;i<K;i++)
g[i] = 1LL*g[i]*ifct[i]%MOD;
}
int ans[MAXN + 5];
int main() {
init();
scanf("%d%d%lld", &N, &K, &T), K++, T++, T %= MOD;
for(int i=0;i<N;i++)
scanf("%d", &a[i]);
func1(); func2(); oper.poly_mul(f, g, ans, K, K, K);
for(int i=0;i<K;i++)
printf("%lld\n", 1LL*fct[i]*ans[i]%MOD);
}

@details@

因为 fft 时我们需要的是 2 的幂的长度,如果不另外建 temp 来存储而是直接在原数组上存的话,需要注意两个数组之间不会互相影响。

所以我也不知道为什么我不建个temp就好了

@codechef - SERSUM@ Series Sum的更多相关文章

  1. MOOCULUS微积分-2: 数列与级数学习笔记 3. Convergence tests

    此课程(MOOCULUS-2 "Sequences and Series")由Ohio State University于2014年在Coursera平台讲授. PDF格式教材下载 ...

  2. 8--Python入门--函数

    函数基本框架如下([]中的内容表示是或选的,可以不写):def 函数名(参数): ['''函数说明文档'''] 函数主体 [return 返回对象] 函数小例子 #我们先定义一个函数 def find ...

  3. Pandas v0.23.4手册汉化

    Pandas手册汉化 此页面概述了所有公共pandas对象,函数和方法.pandas.*命名空间中公开的所有类和函数都是公共的. 一些子包是公共的,其中包括pandas.errors, pandas. ...

  4. Python数据科学手册-Pandas:累计与分组

    简单累计功能 Series sum() 返回一个 统计值 DataFrame sum.默认对每列进行统计 设置axis参数,对每一行 进行统计 describe()可以计算每一列的若干常用统计值. 获 ...

  5. Sum of AP series——AP系列之和

    A series with same common difference is known as arithmetic series. The first term of series is 'a' ...

  6. 深入理解无穷级数和的定义(the sum of the series)

    Given an infinite sequence (a1, a2, a3, ...), a series is informally the form of adding all those te ...

  7. codewars-7kyu:Sum of the first nth term of Series

    Task: Your task is to write a function which returns the sum of following series upto nth term(param ...

  8. CodeChef Sum of distances(分治)

    CodeChef Sum of distances(分治) 题目大意 有一排点,每个点 i 向 \(i + 1, i + 2, i + 3\) 分别连价值为 \(a_i,b_i,c_i\) 的有向边, ...

  9. [LeetCode] #112 #113 #437 Path Sum Series

    首先要说明二叉树的问题就是用递归来做,基本没有其他方法,因为这数据结构基本只能用递归遍历,不要把事情想复杂了. #112 Path Sum 原题链接:https://leetcode.com/prob ...

随机推荐

  1. Djangog写XXOO管理的要求以及思路

  2. ScrollView 实现子视图滑动到顶部时固定不动

    这个,个人建议使用自己写的布局使用view的gon或者visble的方法,使用design包中的控件来的话,局限性很大 方法有倆 (1)自定义ScrollView 重写ScrollView 的 com ...

  3. 一文读懂JVM

    (一)JVM 基础知识 1)Java 是如何实现跨平台的? 注意:跨平台的是 Java 程序,而不是 JVM.JVM 是用 C/C++ 开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本 ...

  4. 关于sublime text2的一些问题(为解决)

    1. 编写markdown文件后,如何转成pdf ? 2. 执行程序时,如何在控制台输入数据?

  5. 数据挖掘python,java

    互联网公司zamplus诚聘以下职位: (1)数据挖掘工程师 (Location:上海.北京) 岗位职责: 1. 研究基于sponsored search, content match和behavio ...

  6. Thrift源码分析(一)-- 基本概念

    我所在的公司使用Thrift作为基础通信组件,相当一部分的RPC服务基于Thrift框架.公司的日UV在千万级别,Thrift很好地支持了高并发访问,并且Thrift相对简单地编程模型也提高了服务地开 ...

  7. 2018年DDoS攻击全态势:战胜第一波攻击成“抗D” 关键

    2018年,阿里云安全团队监测到云上DDoS攻击发生近百万次,日均攻击2000余次.目前阿里云承载着中国40%网站,为全球上百万客户提供基础安全防御.可以说,阿里云上的攻防态势是整个中国攻防态势的缩影 ...

  8. Selenium-Switch与SelectApi接口

    Switch 我们在UI自动化测试时,总会出现新建一个tab页面.弹出一个浏览器级别的弹框或者是出现一个iframe标签,这时我们用WebDriver提供的Api接口就无法处理这些情况了.需要用到Se ...

  9. (四)IO流之InputStream和OutputStream

    InputStream:定义了字节输入流的抽象类 OutputStream:定义了字节输出流的抽象类;该类所有方法返回void值 FileInputStream:继承InputStream FileO ...

  10. Java排序需掌握算法 详解

    package com.sxt.review; /*内部排序:(在内存) * 插入排序-->希尔排序 * 冒泡排序-->快速排序 * 选择排序-->堆排序 * 归并排序 * 基数排序 ...