• @codechef



    @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 ot =j}^{N}a_i^{x-1}*a_j ]

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

    [Q(x-2)*R(2) = sum_{i=1}^{N}sum_{j=1,i ot =j}^{N}a_i^{x-1}*a_j + sum_{i=1}^{N}sum_{j=1,i ot =j}^{N}sum_{k=1,i ot =k,j ot =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
    ", 1LL*fct[i]*ans[i]%MOD);
    }
    

    @details@

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

  • 相关阅读:
    Spring Boot 结合 Redis 序列化配置的一些问题
    基于SpringBoot的代码在线运行的简单实现
    将Spring实战第5版中Spring HATEOAS部分代码迁移到Spring HATEOAS 1.0
    用Spring Security, JWT, Vue实现一个前后端分离无状态认证Demo
    使用最新AndroidStudio编写Android编程权威指南(第3版)中的代码会遇到的一些问题
    C# 内存管理优化畅想----前言
    C# 内存管理优化实践
    C# 内存管理优化畅想(三)---- 其他方法&结语
    C# 内存管理优化畅想(二)---- 巧用堆栈
    C# 内存管理优化畅想(一)---- 大对象堆(LOH)的压缩
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11231986.html
Copyright © 2020-2023  润新知