F - Wine Thief
题目描述
给定一个长度为 n n n 的序列 a a a ,从中选两两不相邻的 k k k 个数,价值为这 k k k 个数的和,求所有合法方案的价值总和对 998244353 998244353 998244353 取模。
数据范围与提示
2 ≤ n ≤ 3 e 5 , 1 ≤ k ≤ ⌈ n 2 ⌉ 2\le n\le 3e5,1\le k\le \lceil \frac{n}{2}\rceil 2≤n≤3e5,1≤k≤⌈2n⌉ 。
思路
多亏了 这篇博客 的启发。
首先想到把每个数的贡献分开求,设
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 表示
n
n
n 个数中选
k
k
k 个,必须选第
i
i
i 个数的合法方案数,那么答案就是
∑
i
=
1
n
a
i
∗
G
(
n
,
k
,
i
)
\sum_{i=1}^na_i*G(n,k,i)
i=1∑nai∗G(n,k,i)
然而
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 非常不好求,因为要考虑每个数前面和后面选了多少个。如果能够找到每个
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 之间的数量关系就可以先求出其中一个值继而求出其余所有。
根据经验,我们可以通过把序列化成环达到这个目的 (经验就是不讲道理) 。
如果把序列化成环,那么就只多了一个限制: a 1 a_1 a1 和 a n a_n an 不能同时选。
我们把 G ( n , k , i ) G(n,k,i) G(n,k,i) 的定义放到环上变为 G ′ ( n , k , i ) G'(n,k,i) G′(n,k,i) ,那么由于环上的方案旋转同构,所以对于任意 i i i , G ′ ( n , k , i ) G'(n,k,i) G′(n,k,i) 是相等的。
为了方便,我们定义 f ( n , k ) f(n,k) f(n,k) 为 n n n 个数中选 k k k 个数的方案数,那么把数与数之间的空去掉,可得 f ( n , k ) = C n − k + 1 k f(n,k)=C_{n-k+1}^{k} f(n,k)=Cn−k+1k ;定义 G ′ ( n , k , i ) = F ( n , k ) G'(n,k,i)=F(n,k) G′(n,k,i)=F(n,k) ,讨论一下可得 F ( n , k ) = { [ k = = 1 ] n < 3 f ( n − 3 , k − 1 ) n ≥ 3 F(n,k)=\left\{\begin{matrix}[k==1] & n<3\\ f(n-3,k-1) & n≥3\end{matrix}\right. F(n,k)={[k==1]f(n−3,k−1)n<3n≥3 。
现在我们考虑把这个限制去掉,那么只需要加上
a
1
a_1
a1 和
a
n
a_n
an 同时选的情况。而这种情况相当于固定了两端的四个点,而中间的
n
−
4
n-4
n−4 个点又构成了一个子问题,所以我们可以得到
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 的递推式:
G
(
n
,
k
,
i
)
=
{
0
i
≤
0
F
(
n
,
k
)
+
f
(
n
−
4
,
k
−
2
)
i
=
1
F
(
n
,
k
)
+
G
(
n
−
4
,
k
−
2
,
i
−
2
)
i
>
1
G
(
n
,
k
,
n
−
i
+
1
)
i
>
⌈
n
2
⌉
G(n,k,i)=\left\{\begin{matrix} 0 & i\le 0 \\F(n,k)+f(n-4,k-2) & i=1 \\F(n,k)+G(n-4,k-2,i-2) & i>1 \\G(n,k,n-i+1) & i>\lceil\frac{n}{2}\rceil \end{matrix}\right.
G(n,k,i)=⎩⎪⎪⎨⎪⎪⎧0F(n,k)+f(n−4,k−2)F(n,k)+G(n−4,k−2,i−2)G(n,k,n−i+1)i≤0i=1i>1i>⌈2n⌉
现在问题的症结就在如何快速求
i
>
1
i>1
i>1 的情况,通过把它展开,我们发现
G
(
n
,
k
,
i
)
=
F
(
n
,
k
)
+
F
(
n
−
4
,
k
−
2
)
+
F
(
n
−
8
,
k
−
4
)
+
.
.
.
G(n,k,i)=F(n,k)+F(n-4,k-2)+F(n-8,k-4)+...
G(n,k,i)=F(n,k)+F(n−4,k−2)+F(n−8,k−4)+...
因此,对于不同的
i
i
i ,
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 的前面部分完全一样,我们只需要预处理出前缀和,然后对每个
G
(
n
,
k
,
i
)
G(n,k,i)
G(n,k,i) 特判一次即可。
现在唯一的问题就是,这么递归下去求值的时候,很可能会出现算组合数里其中一个参数小于0的情况,而这种情况在上面的所有式子中都是没有意义的,所以取0即可。
剩下的代码就非常好打了,只要把每个函数里写好就行。常数随便大,因为是 O ( n ) O(n) O(n) 。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<ctime>
#include<map>
#define ll long long
#define MAXN 300005
#define uns unsigned
#define INF 0x7f7f7f7f
#define lowbit(x) ((x)&(-(x)))
#define MOD 998244353ll
using namespace std;
inline ll read(){
ll x=0;bool f=1;char s=getchar();
while((s<'0'||s>'9')&&s>0){if(s=='-')f^=1;s=getchar();}
while(s>='0'&&s<='9')x=(x<<1)+(x<<3)+s-'0',s=getchar();
return f?x:-x;
}
inline ll ksm(ll a,ll b,ll mo){
ll res=1;
for(;b;b>>=1,a=a*a%mo)if(b&1)res=res*a%mo;
return res;
}
int n,k,D;
ll a[MAXN],fac[MAXN],inv[MAXN];
inline ll C(int n,int m){
if(m<0||m>n)return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
inline ll gf(int n,int k){return C(n-k+1,k);}
inline ll gF(int n,int k){
if(n<3)return k==1;
else return gf(n-3,k-1);
}
ll sf[MAXN];
inline ll G(int n,int k,int i){
if(i<1)return 0;
else if(i==1)return (gF(n,k)+gf(n-4,k-2))%MOD;
else if(i>((n+1)>>1))return G(n,k,n-i+1);
else return (sf[i>>1]+G(n-((i>>1)<<2),k-((i>>1)<<1),i&1))%MOD;
}
int main()
{
n=read(),k=read(),D=read();
for(int i=1;i<=n;i++)a[i]=read();
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%MOD;
inv[n]=ksm(fac[n],MOD-2,MOD);
for(int i=n-1;i>1;i--)inv[i]=inv[i+1]*(1ll+i)%MOD;
for(int i=1;i<=n;i++)sf[i]=(sf[i-1]+gF(n-((i-1)<<2),k-((i-1)<<1)))%MOD;
ll ans=0;
for(int i=1;i<=n;i++)
ans=(ans+a[i]*G(n,k,i)%MOD)%MOD;
printf("%lld\n",ans);
return 0;
}