2014多校8(1001)hdu4945(dp+组合数计数+求逆元)

2048

Time Limit: 3000/1500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 566    Accepted Submission(s): 129


Problem Description
Teacher Mai is addicted to game 2048. But finally he finds it's too hard to get 2048. So he wants to change the rule:

You are given some numbers. Every time you can choose two numbers of the same value from them and merge these two numbers into their sum. And these two numbers disappear meanwhile.
  
If we can get 2048 from a set of numbers with this operation, Teacher Mai think this multiset is good.

You have n numbers, A 1,...,A n. Teacher Mai ask you how many subsequences of A are good.

The number can be very large, just output the number modulo 998244353.
 

Input
There are multiple test cases, terminated by a line "0".

For each test case, the first line contains an integer n (1<=n<=10^5), the next line contains n integers a i (0<=a i<=2048).
 

Output
For each test case, output one line "Case #k: ans", where k is the case number counting from 1, ans is the number module 998244353.
 

Sample Input
   
   
4 1024 512 256 256 4 1024 1024 1024 1024 5 1024 512 512 512 1 0
 

Sample Output
   
   
Case #1: 1 Case #2: 11 Case #3: 8

题意:给出n个数字(0~2048),求能够用这些数字组成的集合中能够找出若干个数的和为2048的集合数量

思路:首先要保证能组成2048,那么这些数只能形如2的x次方,所以可以将不是2的幂的数先给删掉,最后求总的数量的时候再算上就好了

然后现在就只剩下形如2的x次方的数了,很容易看出,如果一个集合的总和大于或等于2048,那么一定可以在这个集合中找出一堆数去组成2048

这样我们就可以求反面,即求出总和小于2048的集合数量,可以用dp[i][j]表示选完了所有的从2的0次方到2的i次方的数,总和在[j*2的i次方,(j+1)*2的i次方)范围的集合数量,

转移方程为dp[i][j]=dp[i][j]+(dp[i-1][(j-k)*2]+dp[i-1][(j-k)*2+1])*C(n,k),其中n为2的i次方的数一共有多少个,k为当前要选的2的i次方数的数量,显然最后答案为total-dp[11][0],

其中total为所有形如2的x次方的数能够组成的集合数量

最后算上之前剔除的数即可

#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
using namespace std;
#define mod 998244353

typedef __int64 ll;
ll p[100010];
ll pinv[100010];
int a[2050];
int val[15];
int dp[12][2050];
int n;

ll pow_mod(ll aa,ll p)
{
    ll temp=1;
    ll a=aa;
    int cnt=0;
    while(p)
    {
        if((1<
       
       
         '9')&&c!='-'); if(c=='-') { neg=true; while((c=getchar())<'0'||c>'9'); } x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; if(neg) x=-x; } ll diao(ll a,ll b) { a+=b; if(a>mod)a-=mod; return a; } int mini(int x,int y) { return x 
         
       
      
      
     
     
    
    
   
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值