• 题解 luogu P6620 [省选联考 2020 A 卷] 组合数问题


    传送门

    提供一个不用下降幂也不用斯特林数的做法


    【分析】

    (egin{aligned} &sum_{k=0}^n f(k)cdot x^k cdot inom n k \\=&sum_{k=0}^n sum_{i=0}^m a_ik^icdot x^k cdot inom n k \\=&sum_{i=0}^m a_i(sum_{k=0}^n k^icdot x^k cdot inom n k) end{aligned})

    (displaystyle g_t=sum_{k=0}^n k^tcdot x^k cdot inom n k) ,结果显然为 (displaystyle sum_{i=0}^m a_ig_i)

    现考虑如何求解 (g_t)

    (egin{aligned} &sum_{k=0}^n k^tcdot x^k cdot inom n k \\=&sum_{k=0}^n k^{t-1}kcdot x^{k-1} cdot xcdot inom n k \\=&xcdot { ext dover ext dx}(sum_{k=0}^n k^{t-1}cdot x^kcdot inom n k) end{aligned})

    (p(x)=x+1)(g_0=p^n(x))(displaystyle g_t=xcdot { ext dover ext dx}g_{t-1})

    考虑到求导的线性性质,有:

    (egin{aligned} &xcdot { ext dover ext dx}f_np^n(x) \\=&f_ncdot [p(x)-1]cdot np^{n-1}(x) \\=&nf_ncdot p^n(x)-nf_ncdot p^{n-1}(x) end{aligned})

    (g_t) 本质上就是一个关于 (p(x)) 的多项式,只要将求导法则重定义为上述求导法则即可

    故初始化多项式为 (f_n=1) ,之后每次都按上述法则求导,再求值,即可求出 (g_0)~(g_t)

    (n) 非常大,无法直接存下。考虑到初始时最低次项为 (p^n(x)) ,求导一次后最低次项为 (p^{n-1}(x)) ,迭代可得,求导 (m) 次后最低次项为 (p^{n-m}(x))

    由于 (mleq 10^3) ,所以直接开 (O(m)) 的空间,计算的时候偏移 ((n-m)) 即可;同时,每次计算的时候从 ((n-m)) 次方开始带值即可

    故总复杂度为 (O(m^2))


    【代码】

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<ll, ll> pii;
    typedef double db;
    #define fi first
    #define se second
    const int MAXN=1024;
    int n, x, p, m, powit[MAXN], a[MAXN], f[MAXN], g[MAXN];
    inline ll fpow(ll a,ll x) { ll ans=1; for(;x;x>>=1,a=a*a%p) if(x&1) ans=ans*a%p; return ans; }
    inline void init() {
    	cin>>n>>x>>p>>m;
    	powit[0]=fpow(x+1, n-m);
    	for(int i=1; i<=m; ++i)
    		powit[i]=(ll)powit[i-1]*(x+1)%p;
    	f[m]=1;
    	g[0]=powit[m];
    	for(int t=1; t<=m; ++t) {
    		f[0]=0;
    		for(int i=1; i<=m; ++i)
    			f[i-1]=(f[i-1]-(f[i]=(ll)f[i]*(n-m+i)%p))%p;
    		for(int i=0; i<=m; ++i)
    			g[t]=(g[t]+(ll)f[i]*powit[i])%p;
    	}
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0); cout.tie(0);
    	init();
    	int res=0;
    	for(int i=0; i<=m; ++i)
    		cin>>a[i], res=(res+(ll)a[i]*g[i])%p;
    	res=(res+p)%p;
    	cout<<res;
    	cout.flush();
    	return 0;
    }
    

    运算过程中,只要保证运算结果在 ((-p,p)) 范围内,最后统一转入 (Z_p) 范围内

  • 相关阅读:
    页面上传图片后立即预览
    MySQL用户管理
    两种方式实现java生成Excel
    总结个关于MySQL数据库的问题
    Properties操作指南
    Python笔记之不可不练
    Python笔记之不可不知
    SpringMVC一路总结(三)
    SpringMVC一路总结(二)
    SpringMVC一路总结(一)
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/15315671.html
Copyright © 2020-2023  润新知