hdu 4945 2048 (dp+组合的数目)

2048

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


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
Hint
In the first case, we should choose all the numbers. In the second case, all the subsequences which contain more than one number are good.
 

Source



题意:
给你n个数。从中选择一些数能构成一个序列,对于一个序列。能够进行这种操作:选择两个同样的数。将它们替换为它们的和。假设一个序列能够得到2048。那么就说这个序列式good的。问这n个数有多少个子序列是good 的。


思路:
由于每次选择同样的两个数变为他们的和,终于变为2048,所以仅仅有2的幂是有效的,其它的数(tot个)每一个都有两种状态。加或者不加,最后的种数*2^tot。
问题变为给你一些2的幂,问有多少种选取方式终于可变为2048或者2048以上。

能够联想的dp+组合。

dp[i][j]表示处理到2^i,和为[j*2^i,(j+1)*2^i )的个数,枚举选取k个2^(i+1)进行转移就可以。
2048或者2048以上的状态时间复杂度较高,所以能够算出小于2048的状态。总方案数减去它即可了。

此题时间卡的紧,须要预处理出来逆元。组合数自己递推的时候除法变为乘法。然后还要加输入优化~

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#include <sstream>
#define maxn 100005
#define MAXN 100005
#define mod 998244353
#define INF 0x3f3f3f3f
#define pi acos(-1.0)
#define eps 1e-8
typedef long long ll;
using namespace std;

int n,m,tot;
int mp[2050],cnt[13],p[13],ed[13];
ll ans,dp[13][2050];
ll inv[100005];
int a[12]= {2048,1024,512,256,128,64,32,16,8,4,2,1};

void scanf_(int&ret)
{
    char c;
    ret=0;
    while((c=getchar())<'0'||c>'9');
    while(c>='0'&&c<='9') ret=ret*10+(c-'0'),c=getchar();
}
ll pow_mod(ll x,ll n)
{
    ll res = 1;
    while(n)
    {
        if(n&1) res = res * x %mod;
        x = x * x %mod;
        n >>= 1;
    }
    return res;
}
void egcd(ll a,ll b,ll &x,ll &y)
{
    if (b==0)
    {
        x=1,y=0;
        return ;
    }
    egcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-a/b*x;
}
void solve()
{
    int i,j,k;
    memset(dp,0,sizeof(dp));
    for(i=0; i<=11; i++)
    {
        ed[i]=min(cnt[i],a[i]-1);
    }
    dp[0][0]=1;
    ll h=1;
    for(i=1; i<=ed[0]; i++)
    {
        h=((h*(cnt[0]-i+1))%mod)*inv[i]%mod;
        dp[0][i]=h;
    }
    for(i=0; i<11; i++)
    {
        for(j=0; j<a[i]; j++)
        {
            if(!dp[i][j]) continue ;
            h=1;
            dp[i+1][j>>1]+=dp[i][j];
            dp[i+1][j>>1]%=mod;
            for(k=1; k<=ed[i+1]; k++)
            {
                if((j>>1)+k<a[i+1])
                {
                    h=((h*(cnt[i+1]-k+1))%mod)*inv[k]%mod;
                    dp[i+1][(j>>1)+k]+=h*dp[i][j];
                    dp[i+1][(j>>1)+k]%=mod;
                }
                else break ;
            }
        }
    }
    ans=((pow_mod(2,n-tot)-dp[11][0]+mod)%mod)*pow_mod(2,tot);
    ans%=mod;
}
int main()
{
    int i,j,u,test=0;
    for(i=1; i<=100000; i++)
    {
        ll x,y;
        egcd(i,mod,x,y);
        x=(x+mod)%mod;
        inv[i]=x;
    }
    memset(mp,-1,sizeof(mp));
    p[0]=1;
    mp[1]=0;
    for(i=1; i<=11; i++)
    {
        p[i]=p[i-1]*2;
        mp[p[i]]=i;
    }
    while(1)
    {
        scanf_(n);
        if(n==0) break ;
        tot=0;
        memset(cnt,0,sizeof(cnt));
        for(i=1; i<=n; i++)
        {
            scanf_(u);
            if(mp[u]!=-1) cnt[mp[u]]++;
            else tot++;
        }
        solve();
        printf("Case #%d: %I64d\n",++test,ans);
    }
    return 0;
}
/*
11
256 256 256 256 256 256 512 512 1024 1024 2048
*/

 

版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值