ARC120F-Wine Thief(非F2)——序列化环

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 2n3e51k2n

思路

多亏了 这篇博客 的启发。

首先想到把每个数的贡献分开求,设 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=1naiG(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)=Cnk+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(n3,k1)n<3n3

现在我们考虑把这个限制去掉,那么只需要加上 a 1 a_1 a1 a n a_n an 同时选的情况。而这种情况相当于固定了两端的四个点,而中间的 n − 4 n-4 n4 个点又构成了一个子问题,所以我们可以得到 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(n4,k2)F(n,k)+G(n4,k2,i2)G(n,k,ni+1)i0i=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(n4,k2)+F(n8,k4)+...
因此,对于不同的 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值