LGP8340题解

太弱小了,没有力量。。。/ll

如果正着做只有一 \(O(n^2)\) 的 DP:\(dp[i][j]\) 表示 \([1,i]\) 能凑出 \([1,j]\) 中所有数的方案数。

这太麻烦了,而且看上去就不可优,考虑计算不合法的。

枚举第一个不合法的位置,设 \(f_i\) 表示在 \([1,i]\) 中选能凑出 \([1,i]\) 中的所有数但是凑不出 \(i+1\) 的方案数,然后强制不选 \(i+1\),答案就是 \(2^n-\sum_{i=1}^{n-1}f_i2^{n-i-1}\)

那么很显然 \(f_i\) 一定是一个关于 \(i\) 的拆分数。先设 \(g_i\) 表示 \(i\) 的互异拆分数。

然后发现直接算 \(f\) 又很麻烦,于是考虑再次算不满足的。

\(w(i,j)\) 表示在 \([i,j]\) 中的数选择一部分,使其和为 \(j-i+1\) 的方案数。

那么有 \(f_n=g_n-\sum_{i=1}^{n-1}f_i\times w(i+2,n)\)越写越复杂

考虑 \(w(i,j)=[x^{j-i+1}]\prod_{k=i}^{j}(1+x^i)\)。对于一个互异拆分数,枚举其左上角的三角形,可以将剩下部分变成拆分数。这里只是所有数都不小于 \(j\),所以在枚举三角形时顺便带掉即可。

需要注意的是相当于令拆分数的大小不能超过 \(k\),所以还需要通过共轭转化成最大元素为 \(k\) 的拆分数。

所以有:

\[w(l,r)=[x^{r-l+1}]\sum_{k=1}x^{\frac{k(k+1)}{2}+(l-1)k}\prod_{i=1}^{k}\frac{1}{1-x^i} \]

然后就有:

\[f_n=g_n-\sum_{i=1}^{n-1}f_ix^i\sum_{k=1}x^{\frac{k(k+1)}{2}}\times x^{(i+1)k}\prod_{i=1}^{k}\frac{1}{1-x^i} \]

\[f_n=g_n-\sum_{k=1}x^{\frac{k(k+1)}{2}}\sum_{i=1}^{n-1}f_ix^{(i+1)k+i}\prod_{j=1}^{k}\frac{1}{1-x^j} \]

注意到一件事,对于 \(f_i\),下一个要选的元素一定大于 \(i+1\),所以对 \(f_n\) 有贡献的 \(f_i\) 满足 \(i<\lfloor\frac{n}{2}\rfloor\)

于是考虑类似倍增的方法,每次计算 \([1,\lfloor\frac{n}{2}\rfloor]\)\(f\) 然后再来计算后半段。

设:

\[F(x)=\sum f_ix^i \]

\[S_k(x)=\sum_{i=k}x^{\frac{i(i+3)}{2}}F(x^{i+1})\prod_{j=k}^{i}\frac{1}{1-x^j} \]

那么有:

\[S_k(x)=S_{k+1}(x)\frac{1}{1-x^k}+x^{\frac{k(k+3)}{2}}F(x^{k+1}) \]

复杂度 \(T(n)=T(\frac{n}{2})+O(n\sqrt{n})=O(n\sqrt{n})\),常数不大,可以通过。

做完这题之后就感觉自己根本不会数学。。。被打自闭了。。。

#include<cstdio>
#include<cmath>
const int M=5e5+5;
int n,P,f[M],g[M];
inline int Add(const int&a,const int&b){
	return a+b>=P?a+b-P:a+b;
}
inline int Del(const int&a,const int&b){
	return b>a?a-b+P:a-b;
}
inline void Solve(const int&n){
	if(n<2)return;Solve(n>>1);for(int i=0;i<=n;++i)g[i]=0;
	for(int x=sqrt(n*2),y=x*(x+1)>>1;x>=1;y-=x--){
		for(int i=x+y,j=0;i<=n;i+=x+1,++j)g[i]=Add(g[i],f[j]);
		for(int i=x+x+y,j=x+y;i<=n;++i,++j)g[i]=Add(g[i],g[j]);
	}
	for(int i=n/2+1;i<=n;++i)f[i]=Del(f[i],g[i]);
}
signed main(){
	int ans(1);scanf("%d%d",&n,&P);f[0]=g[0]=1;
	for(int x=1,y=1;y<=n;y+=++x){
		for(int i=x,j=0;i<=n-y;++i,++j)g[i]=Add(g[i],g[j]);
		for(int i=y,j=0;i<=n;++i,++j)f[i]=Add(f[i],g[j]);
	}
	Solve(n-1);for(int i=0;i<n;++i)ans=Add(ans,ans),ans=Del(ans,f[i]);printf("%d",ans);
}
posted @ 2022-07-21 16:41  Prean  阅读(15)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};