HDU - 6042(生成函数)

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 6042(生成函数)相关的知识,希望对你有一定的参考价值。

题意:一个体积为2∗n的背包,有n(n⩽5e4)种食物,第i种食物的体积是i,数量是ai(0⩽a1<a2<⋯<an⩽2n),还有m种装备,第i种装备的体积是bi(1⩽bi⩽2n),求装一些食物和一件装备使得背包装满的方案数。
思路: 生成函数
容易想到所有食物的生成函数为
∏ i = 1 n ( 1 + x i + x 2 i + . . . + x i ∗ a i ) = ∏ i = 1 n 1 − x a i + 1 1 − x i ( m o d x 2 n + 1 ) \\prod_i=1^n(1+x^i+x^2i+...+x^i*a_i)=\\prod_i=1^n\\frac1-x^a_i+11-x^i(mod\\quad x^2n+1) i=1n(1+xi+x2i+...+xiai)=i=1n1xi1xai+1(modx2n+1)
每次询问q,ans= [ x 2 n − q ] [x^2n-q] [x2nq]
将上面的式子分成两部分,
分子部分:
∏ i = 1 n 1 − x a i + 1 ∗ i \\prod_i=1^n1-x^a_i+1*i i=1n1xai+1i,由于ai是严格递增的, ( a i + 1 ) ∗ i > = i 2 (ai+1)*i>=i^2 (ai+1)i>=i2,所以有用的ai只有根号级别的,可以暴力算
分母部分
∏ i = 1 n 1 1 − x i \\prod_i=1^n\\frac11-x^i i=1n1xi1,发现这部分的生成函数的意义是n的拆分数,但是这题要用到的x的次数是0~2n,n的拆分数只能解决0 ~ n的次数。
对式子变换一下
∏ i = 1 2 n 1 1 − x i ∏ i = n + 1 2 n ( 1 − x i ) \\prod_i=1^2n\\frac11-x^i\\prod_i=n+1^2n(1-x^i) i=12n1xi1i=n+12n(1xi)
≡ ∏ i = 1 2 n 1 1 − x i ( 1 − ∏ i = n + 1 2 n x i ) ( m o d x 2 n + 1 ) \\equiv\\prod_i=1^2n\\frac11-x^i(1-\\prod_i=n+1^2nx^i)(mod\\quad x^2n+1) i=12n1xi1(1i=n+12nxi)(modx2n+1)(因为是在模 x 2 n + 1 x^2n+1 x2n+1下意义的生成函数,后面多项式x的次数范围是[n+1,2n],任意两个相乘都超过了x ^ 2n)
这样前半部分可以nsqrt(n)预处理,每个case就可以O(n)做。后面一个多项式,因为次数都是1,可以做到O(n)乘(前缀和优化)。
总复杂度O(qnsqrt(n))

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int N=1e5+5;
const int mod=1e9+7;
const double eps=1e-8;
int n,m,tot;
int a[N],g[N],f[N];
void ini()
    std::vector<pii> V;
    _for(i,1,(ll)1e5)
        if( i*(3*i-1)/2 > 1e5 ) break;
        V.push_back(i*(3*i-1)/2,1);
    
    _for(i,1,(ll)1e5)
        if( i*(3*i+1)/2 > 1e5 ) break;
        V.push_back(i*(3*i+1)/2,-1);
    
    sort(all(V));
    for(int i=0 ;i<(int)V.size() ;i++)
        if( i%4 <= 1 ) V[i].se = -1;
        else V[i].se = 1;
    
    f[0]=1;
    f[1]=1;
    _for(i,2,1e5)
        for(int j=0;j<(int)V.size();j++)
            if( V[j].fi > i ) break;
            f[i] -= (f[i-V[j].fi]*V[j].se%mod+mod)%mod; 
            f[i] += mod;
            f[i] %= mod;
        
    

void solve()
    _for(i,1,n) cin>>a[i];
    fill(g,g+2*n+1,0ll);
    //1 - ∑x^i
    for(int i=0 ;i<n ;i++)
        g[i+n+1]=(mod - f[i]);
    
    _for(i,n+1,2*n) g[i] = (g[i] + g[i-1])%mod;
    //乘上整数划分的数列
    _for(i,0,n<<1) g[i] = (g[i]+f[i])%mod;
    //第一部分
    _for(i,1,n)
        int t = (a[i] + 1)*i;
        if( t > 2*n ) break;
        _rep(j,2*n,0)
            if( j - t < 0 ) break;
            g[j] = (g[j] + mod - g[j-t])%mod;
        
    
    int ans=0;
    while( m-- )
        int x;cin>>x;
        ans = (ans + g[2*n-x])%mod;
    
    cout<<ans<<endl;

signed main()
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif  
    IOS;
    ini();
    while( cin>>n>>m )
        tot++;
        cout<<"Case #"<<tot<<": ";
        solve();
    
    AC; 

以上是关于HDU - 6042(生成函数)的主要内容,如果未能解决你的问题,请参考以下文章

hdu 1028 Ignatius and the Princess III——生成函数

LOOPS HDU - 3853 (概率dp):(希望通过该文章梳理自己的式子推导)

HDU 5828 Rikka with Sequence(线段树 开根号)

hdu 4027 Can you answer these queries? 线段树区间开根号,区间求和

HDU - 2256 矩阵快速幂 带根号的递推

HDU 6756 Finding a MEX 树状数组+根号分治