【题解】[CQOI2008] 矩阵的个数

熊神仙的做法好难想到。。。

暴力转移是这样的: d p i , x , y = ∑ d p i − 1 , x − x 0 , y − y 0 ( x 0 + y 0 ≤ a i ) dp_{i,x,y}=∑dp_{i-1,x-x_0,y-y_0}(x_0+y_0\leq a_i) dpi,x,y=dpi1,xx0,yy0(x0+y0ai)

相当于要对一个等腰 rt 的区域求和。

观察到 d p i , x − 1 , y dp_{i,x-1,y} dpi,x1,y 的重叠部分:

请添加图片描述
d p i , x , y = d p i , x − 1 , y − l i n e ( x − 1 , y − m → x − m − 1 , y ) + l i n e ( x , y → x , y − m ) dp_{i,x,y}=dp_{i,x-1,y}-line(_{x-1,y-m}\to _{x-m-1,y})+line(_{x,y}\to_{x,y-m}) dpi,x,y=dpi,x1,yline(x1,ymxm1,y)+line(x,yx,ym)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e17;
int n,m,s1,s2,s3,sum,a[205];
ll dp[205][205][205],sum1[205][205],sum2[205][205];
void Mod(ll &x,ll y) {
	x=(x+y)%mod;
}
ll Sum1(int x,int y,int x0) {
	if(y<0) x+=y,y=0;
	if(x>=0&&y>=0) {
		if(x0>=1&&x+y-x0+1<=s2) return sum1[x][y]-sum1[x0-1][x+y-x0+1];
		return sum1[x][y];
	}
	return 0;
}
ll Sum2(int x,int y,int y0) {
	if(x>=0&&y>=0) {
		if(y0>=1) return sum2[x][y]-sum2[x][y0-1];
		return sum2[x][y]; 
	}
	return 0;
}
int main() {
//	freopen("data.in","r",stdin);
	scanf("%d%d%d%d",&n,&s1,&s2,&s3);
	dp[0][0][0]=1;
	for(int i=0;i<=s1;i++) {
		for(int j=0;j<=s2;j++) {
			sum1[i][j]=((i>=1)?sum1[i-1][j+1]:0)+dp[0][i][j];
			sum2[i][j]=((j>=1)?sum2[i][j-1]:0)+dp[0][i][j];
		}
	}
	for(int i=1;i<=n;i++) {
		scanf("%d",&m),sum+=m; 
		for(int x=0;x<=s1;x++) {
			for(int y=0;y<=s2;y++) {
				dp[i][x][y]=(((x>=1)?dp[i][x-1][y]:0)-Sum1(x-1,y-m,x-m-1)+Sum2(x,y,y-m))%mod;
			}
		}
		for(int x=0;x<=s1;x++) {
			for(int y=0;y<=s2;y++) {
				sum1[x][y]=(((x>=1)?sum1[x-1][y+1]:0)+dp[i][x][y])%mod;
				sum2[x][y]=(((y>=1)?sum2[x][y-1]:0)+dp[i][x][y])%mod;
			}
		}
	}
	if(sum!=s1+s2+s3) {
		printf("0");
	}
	else {
		printf("%lld",(dp[n][s1][s2]+mod)%mod);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值