JZOJ6365. 【NOIP2019模拟2019.9.20】膜拜大会(fake)

32 篇文章 1 订阅
8 篇文章 0 订阅

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\n");
			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\n",ans*ksm(P(n,m),mo-2)%mo);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值