输出时忘记%lld+打错模数=自爆
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define ll long long
#define db double
#define mkp(x,y) make_pair(x,y)
#define pii pair<int,int>
#define X first
#define Y second
const int N=130;
const ll MOD=100000000000000000LL;
int n,a,b,c,S[N],SS[N];
ll f[N][N][N];
void add(ll &x,ll y){
x+=y;if(x>=MOD)x-=MOD;
}
int main(){
scanf("%d%d%d%d",&n,&a,&b,&c);
int h,i,j,k,i1,j1,k1;f[0][0][0]=1;
rep(h,1,n){
scanf("%d",&S[h]);SS[h]=SS[h-1]+S[h];
}
if(SS[n]!=a+b+c){
puts("0");return 0;
}
rep(h,1,n)
per(i,S[h],0)
per(j,S[h]-i,0)
per(i1,min(a-i,SS[h-1]),0)
per(j1,min(b-j,SS[h-1]-i1),0)
add(f[h][i1+i][j1+j],f[h-1][i1][j1]);
printf("%lld\n",f[n][a][b]);
return 0;
}
这个O(n^5)的代码在simpleoj上跑得飞快,直接A掉。
然后考虑优化。
使用前缀和,将所有对f[h][i][j]有贡献的f[h-1][i1][j1]搞成前缀和。这些i1、j1满足以下条件:
这张图告诉我们,用g[h][k]来存k=i+j时有贡献的f[h-1][i1][j1]的和。转移时只要加入斜斜的那一条就可以了。