Description
- 定义一次对于i位置的操作为a[l]+=a[i],a[r]+=a[i],a[i]=0,其中l和r为i的左边和右边位置(1的左为n,n的右为1),每一个位置只可以操作一次
- 给定一个长度n的序列a,求长度为m的操作序列的个数,使得最终a[1]>=K。模998244353
T<=5,n<=1e5,m<=n-2,ai<=1e9
Solution
- 由于m<=n-2的重要条件,所以如果一些位置对于1有贡献。那么一定是l-n按顺序传递,r-2按顺序传递。
- 而1的选与不选以及跟2、n的位置关系决定了a[1]最后到1位置的贡献为2*a[1]、a[1]或0.
- 所以我们可以分类讨论一下。
- 我们可以设置两个指针l,r,也就是上面的意义。枚举l,另一边找到满足条件最终a[1]>=K的最小的r,这个r是单调不增的。然后再计算这一对l,r的方案数。注意到计算的时候可能还算到了l+1,r的方案数。
- 为了避免算重,规定计算的是左边恰好为l,右边至少为r的方案数,所以对于“恰好”要容斥一下,即用l,r的方案减去l+1,r的方案(l,r方案计算的为至少l,r的)
(1).1位置不选,对于位置1的贡献为sum(l,n)+sum(2,r)+a[1]。注意l,n和2,r的顺序已经确定了。
(2).1位置选,再讨论1,2,n的位置(len1,len2分别表示(n-l+1)和(r-1))
-
1,2,n——从m个选择(len1+len2+1)个,钦定最后一个为n,所以从(len1+len2)中选择(len1+1)个,钦定这里面最后一个为2。1可以出现在len1个中的任意一个。
-
1,n,2——同上
-
2,1,n——同样是钦定顺序,同上,不过钦定1出现在len1+1中的最后一个。
-
n,1,2——同上
-
2,n,1——0
-
n,2,1——0
-
1,n(无2)
-
1,2(无n)
-
n,1——0
-
n,2——0
-
注意K=0要特判。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 100005
#define mo 998244353
#define ll long long
#define li(i) ((i==1)?n:i-1)
#define ri(i) ((i==n)?1:i+1)
using namespace std;
int T,n,m,K,i,j,cnt,l,r,len1,len2;
ll a[maxn],inv[maxn],jc[maxn],ijc[maxn],ans,s[maxn];
ll ksm(ll x,ll y){
ll s=1;
for(;y;y/=2,x=x*x%mo) if (y&1)
s=s*x%mo;
return s;
}
ll P(int n,int m){return (n<m)?0:jc[n]*ijc[n-m]%mo;}
ll C(int n,int m){return (n<m)?0:jc[n]*ijc[m]%mo*ijc[n-m]%mo;}
ll Sum(int l,int r){return s[r]-s[l-1];}
ll F(int len1,int len2){
if (len1+len2>m) return 0;
return P(n-1-len1-len2,m-len1-len2)*C(m,len1)%mo*C(m-len1,len2)%mo;
}
ll G(int len1,int len2){
if (len1+len2+1>m) return 0;
return P(n-1-len1-len2,m-1-len1-len2)*C(m,len1+len2+1)%mo*C(len1+len2,len1+1)%mo*len1%mo;
}
ll H(int len1,int len2){
if (len1+len2+1>m) return 0;
return P(n-1-len1-len2,m-1-len1-len2)*C(m,len1+len2+1)%mo*C(len1+len2,len1+1)%mo;
}
ll S(int len){
return P(n-1-len-1,m-1-len)*C(m,len+1)%mo*len%mo;
}
int main(){
freopen("fake.in","r",stdin);
freopen("fake.out","w",stdout);
inv[0]=inv[1]=1;
for(i=2;i<maxn;i++) inv[i]=inv[mo%i]*(mo-mo/i)%mo;
jc[0]=ijc[0]=1;
for(i=1;i<maxn;i++) jc[i]=jc[i-1]*i%mo,ijc[i]=ijc[i-1]*inv[i]%mo;
scanf("%d",&T);
while (T--){
scanf("%d%d%d",&n,&m,&K);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
if (K==0){
printf("1
");
continue;
}
for(i=1;i<=n;i++) s[i]=s[i-1]+a[i]; ans=0;
for(r=1;r<=1+m;r++) if (s[r]>=K)
{ans+=F(0,r-1)-F(1,r-1);break;}
for(l=n;n-l+1<=m;l--) {
len1=n-l+1,len2=r-1;
while (len1+len2>m||r>1&&Sum(l,n)+Sum(1,r-1)>=K)
r--,len2--;
if (Sum(l,n)+Sum(1,r)>=K)
ans+=F(len1,len2)-F(len1+1,len2);
}
ans=(ans%mo+mo)%mo;
if (m>=3){
r=m-1;
for(l=n;n-l+1<m-1;l--){
len1=n-l+1,len2=r-1;
while (len1+len2>=m||r-1>1&&Sum(l,n)+Sum(2,r-1)+a[1]*2>=K)
r--,len2--;
if (len2&&len1&&Sum(l,n)+Sum(2,r)+a[1]*2>=K){
ans+=G(len1,len2)-G(len1+1,len2);
ans+=G(len2,len1)-G(len2,len1+1);
}
}
r=m-1;
for(l=n;n-l+1<m-1;l--){
len1=n-l+1,len2=r-1;
while (len1+len2>=m||r-1>1&&Sum(l,n)+Sum(1,r-1)>=K)
r--,len2--;
if (len2&&len1&&Sum(l,n)+Sum(1,r)>=K) {
ans+=H(len1,len2)-H(len1+1,len2);
ans+=H(len2,len1)-H(len2,len1+1);
}
}
ans=(ans%mo+mo)%mo;
}
if (m>=2){
for(r=2;r<=m;r++) if (Sum(1,r)>=K)
{ans+=S(r-1);break;}
for(l=n;n-l+1+1<=m;l--) if (Sum(l,n)+a[1]>=K)
{ans+=S(n-l+1);break;}
}
printf("%lld
",ans*ksm(P(n,m),mo-2)%mo);
}
}