唱、跳、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();
}