Solution
- 一句话题意
给定集合(S)和一个正整数(K),求(且sumlimits_{S'subseteq S且| S'|=K} f(sumlimits_{xin S'}x))
其中(f(i)=2f(i-1)+3f(i-2))
- subtask1 20%
矩乘+大力搜索qwq
-
subtask2和3不太会qwq
-
subtask4 20%
矩乘+大力dp,(f[i][j][k])表示前(i)个数里面选了(j)个,和为(k)的方案数
- 100%
首先这个是一个常系数线性递推的形式,我们尝试求出(f)这个诡异的东西的通项式
(这里貌似是一个套路)
考虑在(f(n)=a^n)这样的形式里面寻找递推式(f(n)=2f(n-1)+3f(n-2))的解
也就是说看是否有(a^n)满足(a^n=2*a^{n-1}+3*a^{n-2})
大力解方程可以得到(a)的两个解(-1)和(3)
也就是说(f(n)=(-1)^n)和(f(n)=3^n)都是满足条件的
那么对于题目中的数列,我们就可以将其写成(f(n)=c_1*(-1)^n+c_2*3^n)的形式
(对于任意的常数(c_1)和(c_2),(f(n)=c_1*(-1)^n+c_2*3^n)都是满足(f(n)=2f(n-1)+3f(n-2))的)
现在已知(f(0))和(f(1)),我们只要把这两个值带进去再解一下二元一次方程组就可以得到(c_1)和(c_2)的值了
这样就得到了通项公式
现在考虑怎么算这个式子
首先注意到(f(n))由两个部分组成,(c_1*(-1)^n)和(c_2*3^n),这两个部分是可以先单独算出,最后再加起来得到最终答案的,由于两部分的形式类似,我们可以用相同的方式来求解
以下讨论如何求解(c_2*3^n),(c_1*(-1)^n)的求解是类似的
题目中,(3)的指数(n=sumlimits_{xin S'}x) ,由于(c_2)是常数可以提出来最后再乘上去,所以我们就直接考虑如何求解
那么也就是说,对于集合中的每个元素(S_i),选它会乘上(3^{S_i}),不选的话就什么事情都不会发生,同时我们还要限制选择元素的个数,那其实就可以相当于这样一个问题:
每个元素(S_i)有(1)的费用,并能给总价值乘上(3^{S_i}),求总费用为(k)的条件下,所有方案的价值总和
那么这个就可以用生成函数来做了,将未知数(x)的指数看作所花费用,系数看成价值,答案就相当于若干个((1+3^{S_i}x))的乘积的结果中,(x^k)的系数
那也就是说我们要求出(prod(1+3^{S_i}x)),那么可以用分治FFT来解决(先把左边的结果乘出来再把右边的结果乘出来然后再乘起来得到最后答案)
然后就很愉快滴解决啦(套路题既视感qwq【并不】)
代码大概长这样
(一个小细节,fft最后四舍五入的时候要注意一下负数的情况要处理一下qwq不过lyy大佬说直接用round就好了嗯qwq)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#define vct vector<int>
#define ll long long
#define ld long double
#define MOD 99991
using namespace std;
const int MAXN=1e5+10;
const ld pi=acos(-1);
struct cmplx{/*{{{*/
ld a,b;
cmplx(){}
cmplx(ld _x,ld _y){a=_x; b=_y;}
friend cmplx operator + (cmplx x,cmplx y)
{return cmplx(x.a+y.a,x.b+y.b);}
friend cmplx operator - (cmplx x,cmplx y)
{return cmplx(x.a-y.a,x.b-y.b);}
friend cmplx operator * (cmplx x,cmplx y)
{return cmplx(x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a);}
};/*}}}*/
namespace FFT{/*{{{*/
cmplx A[MAXN*4],B[MAXN*4];
int rev[MAXN*4];
int len;
void prework(vct &a,vct &b,int n){
int bit=0;
for (len=1;len<n;len<<=1,++bit);
rev[0]=0;
for (int i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
for (int i=0;i<len;++i) A[i]=B[i]=cmplx(0,0);
for (int i=0;i<a.size();++i) A[i]=cmplx(a[i],0);
for (int i=0;i<b.size();++i) B[i]=cmplx(b[i],0);
}
void fft(cmplx *a,int op){
for (int i=0;i<len;++i)
if (rev[i]>i) swap(a[rev[i]],a[i]);
cmplx w_n,w,t,u;
for (int step=2;step<=len;step<<=1){
w_n=cmplx(cos(2*pi/step),op*sin(2*pi/step));
for (int st=0;st<len;st+=step){
w=cmplx(1,0);
for (int i=0;i<(step>>1);++i){
t=a[st+i+(step>>1)]*w;
u=a[st+i];
a[st+i]=u+t;
a[st+i+(step>>1)]=u-t;
w=w*w_n;
}
}
}
if (op==1) return;
for (int i=0;i<len;++i) a[i].a/=len;
}
void FFt(vct &a,vct &b,int n){
prework(a,b,n);
fft(A,1);
fft(B,1);
for (int i=0;i<len;++i) A[i]=A[i]*B[i];
fft(A,-1);
}
void debug(vct &a){
prework(a,a,a.size());
fft(A,1);
fft(A,-1);
}
}/*}}}*/
vct b[MAXN*2];
int a[MAXN],val[MAXN];
int n,m,f0,f1,c1,c2,cnt;
ll Ans;
int solve(int l,int r);
int ksm(int x,int y);
ll get_val(ld x);
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int P1,P2;
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i) scanf("%d",a+i);
scanf("%d%d",&f0,&f1);
for (int i=1;i<=n;++i) val[i]=a[i]&1?-1:1;
P1=solve(1,n);
for (int i=1;i<=n;++i) val[i]=ksm(3,a[i]);
P2=solve(1,n);
c2=1LL*(f0+f1)*ksm(4,MOD-2)%MOD;
c1=(f0-c2+MOD)%MOD;
Ans=(1LL*c1*b[P1][m]%MOD+1LL*c2*b[P2][m]%MOD)%MOD;
if (Ans<0) Ans+=MOD;
printf("%d
",Ans);
}
int solve(int l,int r){
if (l==r){
b[++cnt].resize(2);
b[cnt][0]=1;
b[cnt][1]=val[l];
return cnt;
}
int mid=l+r>>1,L,R,len;
L=solve(l,mid);
R=solve(mid+1,r);
len=b[L].size()+b[R].size()-1;
FFT::FFt(b[L],b[R],len);
b[L].resize(len);
for (int i=0;i<len;++i) b[L][i]=get_val(FFT::A[i].a)%MOD;
return L;
}
int ksm(int x,int y){
int ret=1,base=x;
for (;y;y>>=1,base=1LL*base*base%MOD)
if (y&1) ret=1LL*ret*base%MOD;
return ret;
}
ll get_val(ld x){
int mk=1;
if (x<0) x=-x,mk=-1;
return ((ll)(x+0.5))*mk;
}