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+...+xi∗ai)=∏i=1n1−xi1−xai+1(modx2n+1)
每次询问q,ans=
[
x
2
n
−
q
]
[x^2n-q]
[x2n−q]
将上面的式子分成两部分,
分子部分:
∏
i
=
1
n
1
−
x
a
i
+
1
∗
i
\\prod_i=1^n1-x^a_i+1*i
∏i=1n1−xai+1∗i,由于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=1n1−xi1,发现这部分的生成函数的意义是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=12n1−xi1∏i=n+12n(1−xi)
≡
∏
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=12n1−xi1(1−∏i=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(线段树 开根号)