CS Academy Sliding Product Sum(组合数)

题意

有一个长为 N 的序列 A=[1,2,3,,N] ,求所有长度 K 的子串权值积的和,对于 M 取模。

N1018,Kmin(600,n),M1018

题解

一道还有些意思的组合数学题 qwq

一开始觉得这不是 K 次多项式么,直接插值QAQ 发现模数不给,逆元可能都没有,太不友好啦。

ansk 为长度为 k 的子串的贡献和。其实我们就是求对于所有 kKansk 的和。

先推推式子。

ansk=i=kni!(ik)!=k!i=kn(ik)=k!(n+1k+1)

那么我们最后就是求对于所有 kKk!(n+1k+1)

前者很好求,对于后者么。。。组合数,n 好大。。Lucasm 也好大。。。弃疗

但我们会发现 k 其实不是很大QAQ

我们需要知道有这样一个东西

(n+mr)=k=0r(nk)×(mrk)

为什么呢?思考一下组合意义就很明显啦。

n=m 的时候就有

(2nr)=k=0r(nk)×(nrk)

有了这个就很好做啦~

我们维护一个序列 Ai[(i0),(i1),,(iK+1)] 。最后我们要求的就是 AN+1

那么有前面那个式子,我们就可以倍增求出 An 啦。

所以复杂度是 O(k2logn) 的。(默认不适用慢速乘,用 __int128

前面那个是卷积的形式,也可以用 FFT 优化到 O(klogklogn) ,但由于模数很鬼畜,似乎没有那么优秀。

代码

#include <bits/stdc++.h> #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i) #define Set(a, v) memset(a, v, sizeof(a)) #define Cpy(a, b) memcpy(a, b, sizeof(a)) #define debug(x) cout << #x << ": " << (x) << endl using namespace std; using ll = __int128; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; } template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } template<typename T> inline T read() { T x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn; } void File() { #ifdef zjp_shadow freopen ("sliding-product-sum.in", "r", stdin); freopen ("sliding-product-sum.out", "w", stdout); #endif } const int N = 610; ll n, Mod; int k; struct Array { ll a[N]; Array() { Set(a, 0); } inline Array friend operator * (const Array &lhs, const Array &rhs) { Array res; For (i, 0, k) For (j, 0, k - i) res.a[i + j] = (res.a[i + j] + lhs.a[i] * rhs.a[j]) % Mod; return res; } }; ll fac[N]; Array fpm(Array x, ll power) { Array res = x; -- power; for (; power; power >>= 1, x = x * x) if (power & 1) res = res * x; return res; } int main() { File(); n = read<ll>() + 1; k = read<int>() + 1; Mod = read<ll>(); Array base; base.a[0] = base.a[1] = 1; Array prod = fpm(base, n); fac[0] = 1; For (i, 1, k) fac[i] = fac[i - 1] * i % Mod; ll ans = 0; For (i, 2, k) ans = (ans + fac[i - 1] * prod.a[i]) % Mod; printf ("%lld\n", (long long)(ans)); return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/10354906.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(193)  评论(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】
点击右上角即可分享
微信分享提示