nbu 2412 Dice

题目链接:http://acm.nbu.edu.cn/v1.0/Problems/Problem.php?pid=2412


题目大意:

有N个骰子,每个骰子有K个面,分别标号1~K,设每个骰子向上的面的值为fi,如果sum(fi)等于S,那么获得一个分数sco=mult(fi)。

sum(fi)=f1+f2+...+fn

mul(fi)=f1*f2*...*fn


求所有mul(fi)的和对100000007取模结果

时间限制为2s

N (1 ≤ N ≤ 1000) K (1 ≤ K ≤ 1000) S (0 ≤ S ≤ 15000).


题目思路:

首先献上点小提示,能不用到__int64的地方尽量别用,否则会超时。。。

要求所有N个骰子的sum(fi)为S的所有mul(fi)的和,设为ans[n][s]。

显然有ans[n][s]=ans[n-1][s-1]+2*ans[n-1][s-2]+...+k*ans[n-1][s-k]

可以把上式作为状态转移方程:

dp[i][j] = dp[i-1][j-1] + 2*dp[i-1][j-2] + ... + k*dp[i-1][j-k]

如果用该转移方程,不难判断复杂度为O(n*k*s),肯定是超时的。

其实我们仔细观察转移方程的右边可以看成:

= dp[i-1][j-1] + dp[i-1][j-2] + ... + dp[i-1][j-k]

+ dp[i-1][j-2] + dp[i-1][j-3] + ... + dp[i-1][j-k]

.

.

.

+ dp[i-1][j-k]

我们设sum[i][j] = dp[i][j-1] + dp[i][j-2] + ... + dp[i][1]

那么转移方程的右边又可以看成:

= sum[i-1][j-1] - sum[i-1][j-k-1]

sum[i-1][j-2] - sum[i-1][j-k-1]

.

.

.

sum[i-1][j-k] - sum[i-1][j-k-1]

我们再设ssum[i][j] = sum[i][j] + sum[i][j-1] + ... + sum[i][1]

这样转移方程的右边又可以看成

= ssum[i][j-1] - ssum[i][j-k-1] - k*sum[i][j-k-1]

这样我们就把O(k)求dp[i][j],转为了O(1),而算法的整体复杂度也降为了O(n*s),这样就不会超时咯。

但是空间复杂度也是O (n*s)的,但是我们发现,求i只需要知道i-1,所以我们可以用滚动数组来处理,这样空间复杂度就降到了O(2*s)。

需要注意的是j-k-1<1的时候,转移方程略有区别,不过也只是小细节的问题,很容易处理。


代码:

#pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<string>
using namespace std;
#define ll __int64
#define clr(x,c,n) memset(x,c,sizeof(x[0])*(n))
#define clr_all(x,c) memset(x,c,sizeof(x))
#define IT iterator
#define ls rt<<1
#define rs ls|1
#define lson l,mid,ls
#define rson mid+1,r,rs
#define middle l+r>>1
#define MOD 100000007
#define inf 0x3f3f3f3f
#define eps (1e-8)
#define PI 3.1415926535897932384626433832795
#define E 2.7182818284590452353602874713527
template <class T> T _min(T a,T b){return a<b? a:b;}
template <class T> T _max(T a,T b){return a>b? a:b;}
template <class T> T _abs(T a){return a>0? a:-a;}
template <class T> T _mod(T a,T m){return a<m? (a<0? (a%m+m)%m:a):a%m;}
template <class T> T _gcd(T a,T b){while(b){T t=b;b=a%b;a=t;}return a;}
template <class T> void _swap(T &a,T &b){T t=b;b=a;a=t;}
template <class T> void getmax(T &a,T b){a= a>b? a:b;}
template <class T> void getmin(T &a,T b){a= (a!=-1 && a<b)? a:b;}
int TS,cas=1;
const int M=15000+5;
int n,k,s,m,now,pre;
ll dp[2][M],tmp,sum[M];

void run(){
	int i,j;
	scanf("%d%d%d",&n,&k,&s);
	now=0;
	dp[now][0];
	for(i=1;i<=s;i++)
		dp[now][i]=dp[now][i-1]+(i<=k? i:0);
	for(i=2;i<=n;i++){
		pre=now;
		now^=1;
		for(j=1;j<=s;j++){
			if(j<=k) dp[now][j]=(dp[now][j-1]+sum[j-1])%MOD;
			else dp[now][j]=((dp[now][j-1]+sum[j-1]-sum[j-k-1]-dp[pre][j-k-1]*k)%MOD+MOD)%MOD;
			sum[j]=(sum[j-1]+dp[pre][j])%MOD;
		}
	}
	printf("Case %d: %lld\n",cas,((dp[now][s]-dp[now][s-1])%MOD+MOD)%MOD);
}

void presof(){
}

int main(){
	//freopen("input.txt","r",stdin);
    //freopen("output.txt","w",stdout);
	presof();
	//run();
	//while(~scanf("%d",&n)) run();
	for(scanf("%d",&TS),cas=1;cas<=TS;cas++) run();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值