HDU 4945 2048(DP)
HDU 4945 2048
题意:给定一个序列,求有多少个子序列能合成2048
思路:把2,4,8..2048这些数字拿出来考虑就能够了,其它数字不管怎样都不能參与组成。那么在这些数字基础上,dp[i][j]表示到第i个数字,和为j的情况数,然后对于每一个数枚举取多少个,就能够利用组合数取进行状态转移,这里有一个剪枝,就是假设加超过2048了,那么后面数字的组合数的和所有都是加到2048上面,能够利用公式一步求解,这种整体复杂度就能够满足题目了。然后这题时限卡得紧啊。10W内的逆元不先预处理出来就超时。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; typedef long long ll;
const int MOD = 998244353; inline void scanf_(int &num)//无负数
{
char in;
while((in=getchar()) > '9' || in<'0') ;
num=in-'0';
while(in=getchar(),in>='0'&&in<='9')
num*=10,num+=in-'0';
} int n, v[2049], mi[15], m, cnt[15];
int dp[15][2049], mi2[100005], invv[100005];
bool istwo[2049]; void init() {
int num;
m = 0;
memset(cnt, 0, sizeof(cnt));
for (int i = 0; i < n; i++) {
scanf_(num);
if (!istwo[num]) {
m++;
continue;
}
else cnt[v[num]]++;
}
} int inv(int n) {
int ans = 1;
int k = MOD - 2;
while (k) {
if (k&1) ans = (ll)ans * n % MOD;
n = (ll)n * n % MOD;
k >>= 1;
}
return ans;
} int solve() {
memset(dp, 0, sizeof(dp));
dp[0][0] = 1;
for (int i = 1; i <= 12; i++) {
for (int j = 0; j <= 2048; j += mi[i]) {
if (dp[i - 1][j] == 0) continue;
int C = 1, s = 0;
int sum = j;
for (int k = 0; k <= cnt[i]; k++) {
int x = sum;
if (x == 2048) {
dp[i][x] = (ll)dp[i - 1][j] * (mi2[cnt[i]] - s) % MOD + dp[i][x];
if (dp[i][x] < 0) dp[i][x] += MOD;
if (dp[i][x] >= MOD) dp[i][x] -= MOD;
break;
}
if (x % mi[i + 1])
x = x - mi[i];
dp[i][x] = (ll)dp[i - 1][j] * C % MOD + dp[i][x];
if (dp[i][x] >= MOD) dp[i][x] -= MOD;
s += C;
if (s >= MOD) s -= MOD;
C = (ll)C * (cnt[i] - k) % MOD * invv[k + 1] % MOD;
sum += mi[i];
}
}
}
return (ll)dp[12][2048] * mi2[m] % MOD;
} int main() {
memset(istwo, false, sizeof(istwo));
memset(v, -1, sizeof(v));
mi[0] = 0; v[0] = 0;
for (int i = 1, j = 1; i <= 2048; i *= 2, j++) {
istwo[i] = true;
v[i] = j;
mi[j] = i;
}
mi[13] = 4096;
for (int i = 1; i <= 2048; i++) {
if (v[i] == -1)
v[i] = v[i - 1];
}
mi2[0] = 1;
for (int i = 1; i <= 100000; i++) {
invv[i] = inv(i);
mi2[i] = mi2[i - 1] * 2 % MOD;
}
int cas = 0;
while (~scanf("%d", &n) && n) {
init();
printf("Case #%d: %d\n", ++cas, solve());
}
return 0;
}
HDU 4945 2048(DP)的更多相关文章
- HDU 4945 2048 DP 组合
思路: 这个题写了一个背包的解法,超时了.搜了下题解才发现我根本不会做. 思路参见这个: 其实我们可以这样来考虑,求补集,用全集减掉不能组成2048的集合就是答案了. 因为只要达到2048就可以了,所 ...
- HDU 4945 2048(dp)
题意:给n(n<=100,000)个数,0<=a[i]<=2048 .一个好的集合要满足,集合内的数可以根据2048的合并规则合并成2048 .输出好的集合的个数%998244353 ...
- hdu 4945 2048 (dp+组合的数目)
2048 Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submi ...
- HDU 4945 (dp+组合数学)
2048 Problem Description Teacher Mai is addicted to game 2048. But finally he finds it's too hard to ...
- hdu 4123 树形DP+RMQ
http://acm.hdu.edu.cn/showproblem.php? pid=4123 Problem Description Bob wants to hold a race to enco ...
- hdu 4507 数位dp(求和,求平方和)
http://acm.hdu.edu.cn/showproblem.php?pid=4507 Problem Description 单身! 依旧单身! 吉哥依旧单身! DS级码农吉哥依旧单身! 所以 ...
- hdu 3709 数字dp(小思)
http://acm.hdu.edu.cn/showproblem.php?pid=3709 Problem Description A balanced number is a non-negati ...
- hdu 4352 数位dp + 状态压缩
XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- hdu 4283 区间dp
You Are the One Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)T ...
随机推荐
- android学习记录(三)百度地图错误---只有一个电话显示帧,没有地图内容。
安卓开发新手百度地图,刚开始碰到一个问题,没有地图信息,还有就是它只有一帧. 如图所示: 上网寻找说是key的问题,然后又一次申请.还是不行. 最后再次看了自己的Manifest文件,发现自己的< ...
- AngularJS与ASP.NET MVC登录超时解决方案
问题: 1.在Action中判断Ajax请求的方法Request.IsAjaxRequest()始终是false 2.返回给前台StatusCode和HttpUnauthorizedResult,前台 ...
- Android(Lollipop/5.0) Material Design(六) 使用图像
Material Design列 Android(Lollipop/5.0)Material Design(一) 简单介绍 Android(Lollipop/5.0)Material Design(二 ...
- SOA(面向服务的架构)
前言:SOA(面向服务的架构)是目前企业应用开发过程中普遍采用的技术,基于MVC WebAPI三层分布式框架开发,以此适用于企业信息系统的业务处理,是本文论述的重点.此外,插件技术的应用,富客户端JQ ...
- Android中的“再按一次返回键退出程序”实现[转]
用户退出应用前给出一个提示是很有必要的,因为可能是用户并不真的想退出,而只是一不小心按下了返回键,大部分应用的做法是在应用退出去前给出一个Dialog,我觉得这样不太友好,用户还得移动手指去按dial ...
- TCO14 2C L2: CliqueGraph,graph theory, clique
称号:http://community.topcoder.com/stat?c=problem_statement&pm=13251&rd=16017 參考:http://apps.t ...
- .NET中lock的使用方法及注意事项
lock就是把一段代码定义为临界区,所谓临界区就是同一时刻只能有一个线程来操作临界区的代码,当一个线程位于代码的临界区时,另一个线程不能进入临界区,如果试图进入临界区,则只能一直等待(即被阻止),直到 ...
- oracle_自动备份用户数据,删除N天前的旧数据(非rman,bat+vbs)
有时数据没有实时备份恢复那么高的安全性需求,但每天 ,或者定期备份表结构 和数据依旧是很有必要的,介绍一种方法 在归档和非归档模式均可使用的自动备份方法. 预期效果是备份用户下的数据含表结构,备份文件 ...
- HDU 2612 -Find a way (注重细节BFS)
主题链接:Find a Way 题目不难,前几天做,当时准备写双向BFS的,后来处理细节上出了点问题,赶上点事搁置了.今天晚上重写的,没用双向,用了两次BFS搜索,和双向BFS 道理差点儿相同.仅仅是 ...
- 2014年辛星jquery解读第二节
*************jquery的语法****************** 1.jquery是通过选取HTML元素,而且对选取的元素运行某些操作,从而完毕某些特效的. 2.因此,我们在使用jQu ...