2019 南昌网络赛D FFT多个多项式相乘

2019 Asia Nanchang D. Interesting Series

链接:https://nanti.jisuanke.com/t/41351

题意:首先题目给了若干定义  

定义:Fn=(a^n-1)/(a-1) 

定义:s,S为集合

定义:value(s)=F[sum(s)] 

定义:sum(s)=s的元素加法和

定义:Ans(k)=  sum_{s是S的子集}value(s)[|s|=k]

输入:n a Q,接下来是集合S的n个元素,接下来是Q次询问 每次询问输入Ki

输出:K行 Ans(Ki)%100003   n,q<=1e5

思路:先推样例S={3,1,1},我们把N=3,K=1,2,3都手推一次  

对于N=3 : 

K=3:S全选C(3,3)    Ans(3,3)=a^(s1+s2+s3)    ( Ans的第二维表示N)

K=2:S里面3个选2   Ans(2,3)=a^(s1s2)+a^(s2s3)+a^(s1s3)

K=1:S里面3个选1   Ans(1,3)=a^s1+a^s2+a^s3

好像看不出什么

对于N=2:

K=2   Ans(2,2)=a^(s1+s2)=a^s1*a^s2

K=1   Ans(1,2)=a^s1+a^s2

考虑萌新s3带来的影响

Ans(3,3)=Ans(2,2)*a^s3

Ans(2,3)=Ans(1,2)*a^s3+Ans(2,2)

Ans(1,3)=Ans(1,2)+a^s3

感觉是个递推,但n^2 DP显然会T

 

换一种思路:既然是集合选取元素,那么对于K=0,1,2,3 每个元素选/不选都要考虑在内

假设F(x)为选取的情况,x的指数为选了多少个

对于N=3, F3(x)=1+A(1,3)*+ A(2,3)x^2 + A(3,3)*x^3

对于N=2  F2(x)=1+A(1,2)*+ A(2,2)x^2

F3(x)=1+[A(1,2)+a^s3]+  [A(1,2)*a^s3+A(2,2) ]x^2  + [A(2,2)*a^s3]x^3

考虑s3选/不选的影响    F3(x)=F2(x)*([a^s3]x+1)

猜想Fn(x)=([a^si]*x+1)的积  FFT多个多项式相乘即可


注意:不能用双模数NTT 因为(a^si)先取了100003模 导致 分别用两个大模数算卷积不对

然后我就不知道怎么处理了,只好写FFT

另外:官方题解的F(x)是把指数的意义反过来了,最终算出来是一样的

我们上面算的是F(n)=a^n的贡献,后面-1的贡献显然是C(n,k),分母最后*逆元即可

1081ms

#include<bits/stdc++.h>
#define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i))
#define reu(i,l,u) for(int i=(l);(i)<(int)(u);++(i))
#define all(o) (o).begin(), (o).end()

using namespace std;
typedef long long ll;
typedef double Num;    //long double TLE
typedef complex<Num> Cp;
const Num PI = 3.141592653589793238462643383279L;
const int MOD = 100003;

namespace FFT {
    void Fuck(int n, Num theta, Cp a[]) {
        for(int m = n; m >= 2; m >>= 1) {
            int mh = m >> 1;
            Cp thetaI = Cp(0, theta);
            rep(i, mh) {
                Cp w = exp((Num)i*thetaI);
                for(int j = i; j < n; j += m) {
                    int k = j + mh;
                    Cp x = a[j] - a[k];
                    a[j] += a[k];
                    a[k] = w * x;
                }
            }
            theta *= 2;
        }
        int i = 0;
        reu(j, 1, n-1) {
            for(int k = n >> 1; k > (i ^= k); k >>= 1) ;
            if(j < i) swap(a[i], a[j]);
        }
    }
    void Dft(int n, Cp a[]) { Fuck(n, 2 * PI / n, a); }  //不改
    void Idft(int n, Cp a[]) { Fuck(n, -2 * PI / n, a); }//不改

    void Mul(vector<Cp> &v, vector<Cp> &w) {
        int n = 1, vwn = v.size() + w.size() - 1;
        while(n < vwn) n <<= 1;
        v.resize(n), w.resize(n);
        Dft(n, &v[0]);Dft(n, &w[0]);
        rep(i, n) v[i] *= w[i];
        Idft(n, &v[0]);
        rep(i, n) v[i] /= n;
    }
}
namespace DLC{
    vector<vector<int> > All;//多项式
    vector<int> Dfs(int l, int r) {//分治
        using namespace FFT;
        if(r - l == 1) {  //构造多项式
            return All[l];
        }
        int mid = (l + r) / 2;//合并
        vector<int> L = Dfs(l, mid), R = Dfs(mid, r);
        vector<Cp> Lc(all(L)), Rc(all(R));
        Mul(Lc, Rc);
        int n = L.size() + R.size() - 1;
        vector<int> res(n);
        rep(i, n) res[i] = (long long)(Lc[i].real() + .5) % MOD;
        return res;
    }
}

//非FFT
ll qmod(ll a,ll b){ll res=1;while(b){if(b&1)res=res*a%MOD;a=a*a%MOD;b=b>>1;}return res;}

int fac[100005], inv[100005];
void Init(void) {//预处理阶乘 
    fac[0] = 1; int lim = 100001;
    for(int i = 1; i <= lim; i++) fac[i] = 1ll*fac[i - 1]* i%MOD;
    inv[lim] = qmod(fac[lim], MOD - 2);
    for(int i = lim - 1; i >= 0; i--) inv[i] = 1ll*inv[i + 1]*(i + 1)%MOD;//乘 
}
int C(int x, int y) {return y>x?0:(1ll*fac[x]*inv[y]%MOD*inv[x - y]%MOD);}


int main() {
    Init();
    using namespace DLC;
    int N,aa,Q;scanf("%d%d%d", &N,&aa,&Q);
    
    ll inva=qmod(aa-1,MOD-2);

    rep(i, N) {//([a^si]x+1)
        vector<int> A;int t;scanf("%d", &t);
        A.push_back(1);A.push_back(qmod(aa,t));All.push_back(A);
    }

    vector<int> ans = Dfs(0, N);
    rep(ii, Q) {
        int K;
        scanf("%d", &K);
        printf("%d\n", (ans[K]-C(N,K)+MOD)%MOD*inva%MOD);
    }
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值