唱、跳、rap 和篮球「TJOI2019」

题意

有若干个人,喜欢唱跳rap篮球,数量给定。排成一行,如果有连续四个人分别喜欢唱跳rap篮球,那么不合法。求总方案数。


思路

容易想到枚举连续情况,然后删去,但是这样无法解决连续情况外的连续。

所以考虑容斥。

每次容斥的时候如果暴力求出方案会到\(O(n^3)\),所以可以用一个小trick优化一下:枚举哪些部分唱跳,哪些部分rap篮球,然后\(O(1)\)计算区内方案数。(此处是否选择唱跳并不重要,因为是对称的)

代码

#include <bits/stdc++.h>

using namespace std;

namespace StandardIO {

	template<typename T>inline void read (T &x) {
		x=0;T f=1;char c=getchar();
		for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
		for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
		x*=f;
	}

	template<typename T>inline void write (T x) {
		if (x<0) putchar('-'),x*=-1;
		if (x>=10) write(x/10);
		putchar(x%10+'0');
	}

}

using namespace StandardIO;

namespace Project {
	#define int long long
	
	const int N=1001;
	const int MOD=998244353;
	
	int n,a,b,c,d,ans;
	int C[N][N],sum[N][N];
	
	inline int query (int i,int l,int r) {
		if (r<l) return 0;
		if (l<=0) return sum[i][r];
		return (sum[i][r]-sum[i][l-1]+MOD)%MOD;
	}

	inline void MAIN () {
		read(n),read(a),read(b),read(c),read(d),a=min(n,a),b=min(n,b),c=min(n,c),d=min(n,d);
		for (register int i=0; i<=1000; ++i) {
			C[i][0]=sum[i][0]=1;
			for (register int j=1; j<=i; ++j) {
				C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
			}
			for (register int j=1; j<=1000; ++j) {
				sum[i][j]=(sum[i][j-1]+C[i][j])%MOD;
			}
		}
		for (register int i=0; i<=n/4; ++i) {
			int res=0,left=n-i*4;
			for (register int j=0; j<=left; ++j) {
				int rap=left-j;
				res=(res+C[left][j]*query(j,j-a,b)%MOD*query(rap,rap-c,d)%MOD)%MOD;
			}
			res=(res*C[i+left][i])%MOD;
			if (i&1) ans=(ans-res+MOD)%MOD;
			else ans=(ans+res)%MOD;
			--a,--b,--c,--d;
			if (a<0||b<0||c<0||d<0) break;
		}
		write(ans);
	}
	
	#undef int
}

int main () {
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	Project::MAIN();
}

posted @ 2019-09-26 16:54  Ilverene  阅读(317)  评论(0编辑  收藏  举报