hdu 7054 Yiwen with Formula
题意:
- 给定序列 A A A ,求所有子序列和的乘积
分析:
-
若枚举的话,对于每一个数都有取与不取 2 2 2 种状态, O ( 2 n ) . . . O(2^n)... O(2n)... 必然超时
-
取与不取, 01 01 01 两种状态,这不就成了 01 01 01 背包
d p dp dp 求得所有子序列和 O ( n 2 ) O(n^2) O(n2) 还是会超时(代码最下面)
-
不慌,还能进一步优化
01 01 01 背包还可以生成函数:
f = ( 1 + x A 1 ) ( 1 + x A 2 ) ( 1 + x A 3 ) . . . ( 1 + x A n ) f=(1+x^{A_1})(1+x^{A_2})(1+x^{A_3})...(1+x^{A_n}) f=(1+xA1)(1+xA2)(1+xA3)...(1+xAn)
所求 a n s = ∏ k = 2 最 大 子 序 列 和 k k 项 系 数 ans=\prod_{k=2}^{最大子序列和}k^{k项系数} ans=∏k=2最大子序列和kk项系数
现在就是去快速求 f f f ,用FTT加速,当然不能逐个运算, O ( n 2 l o g n ) O(n^2logn) O(n2logn) ,反向优化…
要用 分 治 F F T 分治FFT 分治FFT ,每次先算相邻两个 O ( n l o g 2 n ) O(nlog^2n) O(nlog2n)
再看一下数据 1 e 5 1e5 1e5 , F F T FFT FFT 的精度顶不了,再看模数 998244353 998244353 998244353 就很 n i c e nice nice ,能用 N T T NTT NTT
坑点:并不能用 N T T NTT NTT ,多项式算出来的 k 项 系 数 k项系数 k项系数 是 a n s 的 k 项 指 数 ans的k项指数 ans的k项指数 ,所以根据费马小定理 a n s ans ans 模 m o d mod mod ,指数是模 φ ( m o d ) = m o d − 1 \varphi(mod)=mod-1 φ(mod)=mod−1 , m o d − 1 mod-1 mod−1 之后就不能用 N T T NTT NTT 了,要用任意模数 M T T MTT MTT
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double lld;
const int mo=998244353, N=1e5+5;
struct MTT
{
struct cp
{
lld x,y;
inline void init() { x=y=0; }
cp operator + (const cp& t) const { return {x+t.x,y+t.y}; }
cp operator - (const cp& t) const { return {x-t.x,y-t.y}; }
cp operator * (const cp& t) const { return {x*t.x-y*t.y,x*t.y+y*t.x}; }
}p1[N<<3],p2[N<<3],g[N<<3];
lld Pi=acos(-1);
int rev[N<<3];
void fft(cp *a,int len,int inv)
{
for(int i=0;i<len;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int mid=1;mid<len;mid<<=1)
{
cp Wn=cp({cos(Pi/mid),inv*sin(Pi/mid)});
for(int i=0;i<len;i+=mid*2)
{
cp w=cp({1,0});
for(int j=0;j<mid;j++,w=w*Wn)
{
cp x=a[i+j],y=w*a[i+j+mid];
a[i+j]=x+y,a[i+j+mid]=x-y;
}
}
}
}
int mul(int *as,int *a,int n,int *b,int m,int mo)
{
for(int i=0;i<n;i++) {
int x=a[i];
int aa=x>>15,bb=x&0x7fff;
p1[i]={(lld)aa,(lld)bb};
p2[i]={(lld)aa,-(lld)bb};
}
for(int i=0;i<m;i++) {
int x=b[i];
int aa=x>>15,bb=x&0x7fff;
g[i]={(lld)aa,(lld)bb};
}
int len=2;
while(len < n+m) len <<= 1;
for(int i=0;i<len;i++) rev[i] = (rev[i>>1]>>1)|((i&1)?len>>1:0);
fft(p1,len,1); fft(p2,len,1); fft(g,len,1);
for(int i=0;i<len;i++) g[i].x/=len,g[i].y/=len;
for(int i=0;i<len;i++) p1[i]=p1[i]*g[i],p2[i]=p2[i]*g[i];
fft(p1,len,-1); fft(p2,len,-1);
for(int i=0;i<m+n-1;i++)
{
ll ans=0,a1b1=0,a2b2=0,a1b2=0,a2b1=0;
a1b1=(ll)floor((p1[i].x+p2[i].x)/2+0.49)%mo;
a1b2=(ll)floor((p1[i].y+p2[i].y)/2+0.49)%mo;
a2b1=((ll)floor(p1[i].y+0.49)-a1b2)%mo;
a2b2=((ll)floor(p2[i].x+0.49)-a1b1)%mo;
ans=(((((a1b1<<15)%mo+(a1b2+a2b1))%mo)<<15)%mo+a2b2)%mo;
ans+=mo; ans%=mo;
as[i]=ans;
}
for(int i=0;i<len;i++) { p1[i].init(); p2[i].init(); g[i].init(); }
return n+m-1;
}
}MT;
int ksm(int a,int b)
{
int ans=1;
while(b)
{
if(b&1) ans = 1ll*ans*a%mo;
a=1ll*a*a%mo; b >>= 1;
}
return ans;
}
int bcs, bc[N<<2];
// The sum of ai over all test cases will not exceed 4e5. 所以乘4即可
struct node
{
int *a,len;
void init(int l)
{
a = bc+bcs; len=l; bcs += l; // std的结构体封装的动态分配数组
for(int i=0;i<len;i++) a[i]=0;
a[0]=1; a[len-1]++;
}
void mul(node x)
{
len=MT.mul(a,a,len,x.a,x.len,mo-1);
}
};
int a[N];
node cdq(int l,int r)
{
node ans;
if(l==r)
{
ans.init(a[l]+1);
return ans;
}
int mid = l+r>>1;
ans=cdq(l,mid);
ans.mul(cdq(mid+1,r));
return ans;
}
int main()
{
//ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T, n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
int mn=1e9;
for(int i=1;i<=n;i++) cin>>a[i], mn=min(mn,a[i]);
if(mn==0) { printf("0\n"); continue; }
node res = cdq(1,n);
int ans=1;
for(int i=2;i<res.len;i++) ans=(1LL*ans*ksm(i,res.a[i]))%mo;
printf("%d\n",ans);
}
return 0;
}
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int a[N], dp[N];
signed main()
{
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
int T, n;
cin>>T;
while(T--)
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
dp[0]=1;
for(int i=1;i<=100;i++) dp[i] = 0;
for(int i=1;i<=n;i++)
{
for(int j=100;j>=0;j--)
{
if(j>=a[i]) dp[j] += dp[j-a[i]];
}
}
for(int i=0;i<=100;i++) if(dp[i]) cout<<i<<' '<<dp[i]<<endl;
}
return 0;
}