P4463 [集训队互测 2012] calc
P4463 [集训队互测 2012] calc
题目
一个序列 \(a_1,a_2,\dots,a_n\) 是合法的,当且仅当:
- \(a_1,a_2,\dots,a_n\) 都是 \([1,k]\) 中的整数。
 - \(a_1,a_2,\dots,a_n\) 互不相等。
 
一个序列的值定义为它里面所有数的乘积,即 \(a_1\times a_2\times\dots\times a_n\)。
求所有不同合法序列的值的和对 \(p\) 取模后的结果。两个序列不同当且仅当他们任意一位不同。
对于 \(100\%\) 的数据,\(k \le 10 ^ 9\),\(n \le 500\),\(p \le 10 ^ 9\),保证 \(p\) 为素数,保证 \(n + 1 < k < p\)。
思路
序列 \(a\) 就是从 \([1,k]\) 中选择 \(n\) 个不相同的数,然后排列。
因此我们只需要求出所有从 \([1,k]\) 里面选择 \(n\) 个数的方案权值之和,然后乘上 \(n!\)。
DP 做法
设 \(f_{i,j}\) 表示已经选择了 \(i\) 个数,最大的数不超过 \(j\) 的所有方案权值之和。
有转移:
直接做是 \(O(nk)\) 的。
看到这样的递推式子,每一行都是根据上一行求出来的,我们认为 \(f_{n,x}\) 可以表示成关于 \(x\) 的不知道多少次多项式。记为 \(F_i(x)\)。
假设 \(F_{i-1}(x)\) 是 \(m\) 项,那么 \(F_i(x)\) 是多少项呢?
这是一个点值的递推式子。我们不知道 \(F_i(x-1)\) 是多少项。所以把 \(F_i(x-1)\) 展开得到:
这就相当于对函数 \(F_{i-1}\) 做一个有限积分,然后全体次数加 \(1\),然后常数项为 \(F_i(0)=0\)。所以 \(F_i(x)\) 是 \(m+2\) 次。
所以 \(F_n(x)\) 是一个 \(2n\) 次的多项式。我们递推求出 \(F_n(0)\sim F_n(2n)\),然后使用拉格朗日插值法,求出点值 \(F_n(k)\)。
时间复杂度 \(O(n^2)\)。
code
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace hesitate {
    constexpr int N=505;
    int k,n,p;
    int add(int a,int b) { return a+b>=p ? a+b-p : a+b; }
    void _add(int &a,int b) { a=add(a,b); }
    int mul(int a,int b) { return 1ll*a*b%p; }
    void _mul(int &a,int b) { a=mul(a,b); }
    int ksm(int a,int b=p-2) {
        int s=1;
        while(b) {
            if(b&1) _mul(s,a);
            _mul(a,a);
            b>>=1;
        }
        return s;
    }
    int f[N][N<<1];
    int s;
    int lagrange(int *f,int n,int x) {
        int sum=0;
        rep(i,0,n) {
            int s1=f[i],s2=1;
            rep(j,0,n) if(i^j) {
                _mul(s1,add(x,p-j));
                _mul(s2,add(i,p-j));
            }
            _add(sum,mul(s1,ksm(s2)));
        }
        return sum;
    }
    void main() {
        sf("%d%d%d",&k,&n,&p);
        rep(i,0,min(k,n<<1)) f[0][i]=1;
        rep(i,1,n) {
            rep(j,i,n<<1) {
                f[i][j]=add(f[i][j-1],mul(j,f[i-1][j-1]));
            }
        }
        s=1;
        rep(i,1,n) _mul(s,i);
        pf("%d\n",mul(s,lagrange(f[n],n<<1,k)));
    }
}
int main() {
    #ifdef LOCAL
    freopen("in.txt","r",stdin);
    freopen("my.out","w",stdout);
    #endif
    hesitate :: main();
}
生成函数做法
不难得到我们要的就是:
然后不会,鸽掉。
做法见加强版。
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18655764

                
            
        
浙公网安备 33010602011771号