简单序列问题

Description

给定 \(n,p\),求满足以下条件的长度为 \(n\) 序列 \(a\) 的数量。

  • \(1\le a_1\le a_2\le \cdots\le a_n\le n\)
  • \(\forall k\in[1,n),\sum_{i=1}^k{a_i}>\sum_{i=n-k+2}^{n}{a_i}\)

数据范围:\(n\le333333\)\(9\times10^8\le p\le10^9\)
时间范围:\(2500\operatorname{ms}\)

Solution

好题。我标好题的次数可不多。——我国著名化学家 \(\text{wzl}\) 先生。

  • 为了满足条件一,考虑将 \(a\) 差分,设差分后数组为 \(b\),且 \(b_i=a_{i+1}-a_i\)
  • \(m=\lceil \frac{n}{2}\rceil\)。可以发现,当 \(k=m\) 时满足答案,则其余 \(k\) 都满足答案。
  • 分类讨论,这里只详细讨论 \(n\) 为奇数的情况。
  • \(n\) 为奇数时, 条件二转化为 \(\sum_{i=1}^{m-1}{i\times b_i}+\sum_{i=m}^{n-1}{(n-i+1)\times b_i}<s\),其中 \(s=\sum_{i=0}^{n}{b_i}\)
  • 考虑生成函数。

\[\begin{aligned}F(x)&=\frac{1}{(1-x)(1-x^m)[(1-x^2)\ldots(1-x^{m-1})]^2}\\ &=\left(\frac{(1-x^{m+1})(1-x^{m+2})\ldots}{(1-x)(1-x^2)(1-x^3)\ldots}\right)^2\times(1-x)\times(1-x^m)\end{aligned}\]

  • 观察分式上方,可以发现当 \(p+q\ge n\) 时,\((1-x^{p})(1-x^{q})\equiv{1-x^p-x^q}\pmod{x^n}\),可以通过前缀和求出。
    观察分式下方,发现是五边形数,\((1-x)(1-x^2)\ldots=\sum_{i=0}^{\infty}{(-1)^ix^{\frac{i(3i\pm1)}{2}}}\)\(O(n)\) 项内只有约 \(O(\sqrt n)\) 项非 \(0\),可以 \(O(n\sqrt n)\) 求两次得到,避免 MTT。
  • 答案即为 \(\sum_{i=0}^{n-1}{(n-i)\times [x^i]F(x)}\)
  • \(n\) 为偶数时,同理

\[F(x)=\left(\frac{(1-x^{m+1})(1-x^{m+2})\ldots}{(1-x)(1-x^2)(1-x^3)\ldots}\right)^2\times(1-x) \]

  • 时间复杂度为 \(O(n\sqrt n)\),需要卡常。

Code

#include<cstdio>
using namespace std;
const int maxn=400010;
int n,m,MLY,F[maxn],cnt,ans,k[maxn],G[maxn];
inline int mod(int x){
	if(x<0)return x+MLY;
	if(x>=MLY)return x-MLY;
	return x;
}
inline void GetInv(){
	for(int i=1;i<n;++i)
		for(int j=1;k[j]<=i&&j<=cnt;++j)
			F[i]=mod(F[i]-F[i-k[j]]*G[j]);
}
int main(){
	freopen("sequence.in","r",stdin);
	freopen("sequence.out","w",stdout);
	scanf("%d%d",&n,&MLY);
	m=(n+1)>>1;F[0]=1;
	for(int i=m+1;i<n;++i)F[i]=MLY-2;
	k[0]=0;G[0]=1;
	for(int i=1;i*(3*i-1)/2<n;++i){
		if(i*(3*i-1)/2<n){k[++cnt]=i*(3*i-1)/2;G[cnt]=(i%2)?-1:1;}
		if(i*(3*i+1)/2<n){k[++cnt]=i*(3*i+1)/2;G[cnt]=(i%2)?-1:1;}
	}
	GetInv();GetInv();
	for(int i=n-1;i;--i)F[i]=mod(F[i]-F[i-1]);
	if(n&1)for(int i=n-1;i>=m;--i)F[i]=mod(F[i]-F[i-m]);
	for(int i=0;i<n;++i)ans=mod(ans+1ll*(n-i)*F[i]%MLY);
	printf("%d",ans);
	return 0;
}
posted @ 2021-04-21 21:27  7103  阅读(185)  评论(0)    收藏  举报