bzoj 3851: 2048 dp优化

3851: 2048

Time Limit: 2 Sec  Memory Limit: 64 MB
Submit: 22  Solved: 9
[Submit][Status]

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, A1,...,An. 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 ai
(0<=ai<=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.

  sro卡常数orz。。。。。。

  题解什么的可以参见hdu4945,我用的是组合数,逆元什么的,具体来说,只有2^i的数是有用的(这个地方有点坑,如果用x==x&(-x)判定,则会把0算进去)然后我是枚举每一个数x选了多少个,顶多2048/x个,用组合数优化背包。但是这个办法还是很慢,经过面目全非的常数优化,八中2500ms过的,hdu就根本过不了了。

  这个方法实在太渣,优化后卡时过的,童鞋们最好用其他方法额。   

顺便总结一下这道题用的常数优化技巧:

  1. register 这次我实践证明register是有作用的
  2. [2][n]的二维数组改成两个数组。
  3. 数组下标索引改成指针。
  4. 如果会多次调用几个数的乘积,可以提前预处理出来。
  5. 改变for语句嵌套顺序,省略for内部的条件判断。
  6. 读入优化x*10可改成 (x<<3)+(x<<2)
  7. 少用取模才是终极目标。

/**************************************************************
    Problem: 3851
    User: mhy12345
    Language: C++
    Result: Accepted
    Time:2520 ms
    Memory:17536 kb
****************************************************************/

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAXN 510000
#define deal(x,y) \
        (x)=((x)+(y))%MOD;
inline int nextInt()
{
        register int x=0;
        register char ch;
        while (ch=getchar(),ch<‘0‘ || ch>‘9‘);
        while (x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(),ch<=‘9‘ && ch>=‘0‘);
        return x;
}
const int mod=MOD;
typedef long long qword;
qword pow_mod(qword x,qword y)
{
        qword ret=1;
        while (y)
        {
                if (y&1)ret=ret*x%MOD;
                x=x*x%MOD;
                y>>=1;
        }
        return ret;
}
qword dp[20][2049];
qword fact[MAXN];
int tot[2049];
qword inv[MAXN];
qword val[MAXN];
pair<int,int> pl[MAXN];
int topp=-1;
int main()
{
        //freopen("input.txt","r",stdin);
        register int i,j,k,k2;
        int x,y,z,n,m;
        int nn;
        fact[0]=1;
        for (i=1;i<MAXN;i++)
                fact[i]=fact[i-1]*i%MOD;
        inv[0]=1;
        for (i=0;i<MAXN;i++)
                inv[i]=pow_mod(fact[i],MOD-2);
        int cnt=0;
        register qword *dp1,*dp2;
        register qword a=0;
        while (scanf("%d",&n),cnt++,n)
        {
                printf("Case #%d: ",cnt);
                memset(dp[0],0,sizeof(dp[0]));
                memset(tot,0,sizeof(tot));
                dp[0][0]=1;
                for (i=1;i<=n;i++)
                {
                        x=nextInt();
                        tot[x]++;
                }
                int ttr=0;
                for (i=0;i<=2048;i++)
                        if (!i || i!=(i&(-i)))
                                ttr+=tot[i];
                int cnt=0;
                bool flag=false;
                for (i=1;i<=2048;i<<=1,cnt^=flag)
                {
                        memset(dp[cnt^1],0,sizeof(dp[cnt^1]));
                        dp1=dp[cnt];
                        dp2=dp[cnt^1];
                        flag=false;
                        if (!tot[i])continue;
                        flag=true;
                        for (j=0;j<=tot[i];j++)
                                val[j]=*(fact+*(tot+i)) * *(inv+j)%MOD * *(inv+tot[i]-j)%MOD;
                        for (a=0,j=2048/i+(2048%i!=0);j<=tot[i];j++)
                                a=(a+ * (val+j))%MOD;
                        for (k=2048;k>=0;k--)
                                if (dp1[k])
                                {
                                        for (j=0,k2=k;j<=tot[i] && k2<2048;j++,k2+=i)
                                                        deal(dp2[k2],*(dp1+k) * *(val+j));
                                        qword &b=dp2[2048];
                                        k2-=k;
                                        for (;k2<2048 && j<=tot[i];j++,k2+=i)
                                                        deal(b,*(dp1+k)* *(val+j));
                                        if (j<=tot[i])
                                                deal(b,*(dp1+k)*a);
                                }
                }
                printf("%lld\n",dp[cnt][2048]*pow_mod(2,ttr)%MOD);
        }
}

面目全非版

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAXN 510000
#define deal(x,y) \
        (x)=((x)+(y))%MOD;
inline int nextInt()
{
        register int x=0;
        register char ch;
        while (ch=getchar(),ch<‘0‘ || ch>‘9‘);
        while (x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar(),ch<=‘9‘ && ch>=‘0‘);
        return x;
}
const int mod=MOD;
typedef long long qword;
qword pow_mod(qword x,qword y)
{
        qword ret=1;
        while (y)
        {
                if (y&1)ret=ret*x%MOD;
                x=x*x%MOD;
                y>>=1;
        }
        return ret;
}
qword dp[2][2049];
qword fact[MAXN];
int tot[2049];
qword inv[2049];
pair<int,int> pl[MAXN];
int topp=-1;
int main()
{
        freopen("input.txt","r",stdin);
        int i,j,k,x,y,z,n,m;
        int k2;
        int nn;
        fact[0]=1;
        for (i=1;i<MAXN;i++)
                fact[i]=fact[i-1]*i%MOD;
        inv[0]=1;
        for (i=0;i<MAXN;i++)
                inv[i]=pow_mod(fact[i],MOD-2);
        int a1,a2;
        int cnt=0;
        while (scanf("%d",&n),cnt++,n)
        {
                printf("Case #%d: ",cnt);
                memset(dp,0,sizeof(dp));
                memset(tot,0,sizeof(tot));
                dp[0][0]=1;
                for (i=1;i<=n;i++)
                {
                        x=nextInt();
                        tot[x]++;
                }
                int ttr=0;
                for (i=0;i<=2048;i++)
                        if (!i || i!=(i&(-i)))
                                ttr+=tot[i];
                int cnt=0;
                bool flag=false;
                for (i=1;i<=2048;i<<=1,cnt^=flag)
                {
                        memset(dp[cnt^1],0,sizeof(dp[cnt^1]));
                        flag=false;
                        if (!tot[i])continue;
                        flag=true;
                        qword a=0;
                        for (j=2048/i+(2048%i!=0);j<=tot[i];j++)
                                a=(a+inv[j]*inv[tot[i]-j])%MOD;
                        a=a*fact[tot[i]]%MOD;
                        for (k=2048;k>=0;k--)
                                if (dp[cnt][k])
                                {
                                        for (j=0,k2=k;j<=tot[i] && k2<2048;j++,k2+=i)
                                                        deal(dp[cnt^1][k2],dp[cnt][k]*fact[tot[i]]%MOD*inv[j]%MOD*inv[tot[i]-j]);
                                        qword &b=dp[cnt^1][2048];
                                        k2-=k;
                                        for (;k2<2048 && j<=tot[i];j++,k2+=i)
                                                        deal(b,dp[cnt][k]*fact[tot[i]]%MOD*inv[j]%MOD*inv[tot[i]-j]);
                                        if (j<=tot[i])
                                                deal(dp[cnt^1][2048],dp[cnt][k]*a);
                                }
                }
                printf("%lld\n",dp[cnt][2048]*pow_mod(2,ttr)%MOD);
        }
}

TLE版

时间: 2024-10-09 21:32:06

bzoj 3851: 2048 dp优化的相关文章

HDU4945 2048(dp)

先是看错题意..然后知道题意之后写了发dp..无限TLE..实在是不知道怎么优化了,跑了遍数据是对的,就当作理论AC掉好了.. #pragma warning(disable:4996) #include <iostream> #include <cstring> #include <string> #include <vector> #include <cstdio> #include <queue> #include <al

常见的DP优化类型

常见的DP优化类型 1单调队列直接优化 如果a[i]单调增的话,显然可以用减单调队列直接存f[j]进行优化. 2斜率不等式 即实现转移方程中的i,j分离.b单调减,a单调增(可选). 令: 在队首,如果g[j,k]>=-a[i],那么j优于k,而且以后j也优于k,因此k可以重队列中直接删去.在队尾,如果x<y<z,且g[x,y]<=g[y,z],也就是说只要y优于x一定可以得出z优于y的,我们就删去y. 经过队尾的筛选,我们在队列中得到的是一个斜率递减的下凸包,每次寻找从上往下被-

hdu5009 离散化+dp+优化

西安网络赛C题.先对大数据离散化,dp优化 #include<iostream> //G++ #include<vector> #include<cstdio> #include<algorithm> #include<cstring> #include<string> #include<queue> #include<cmath> using namespace std; const int maxn=512

HDU 4945 2048(DP)

HDU 4945 2048 题目链接 题意:给定一个序列,求有多少个子序列能合成2048 思路:把2,4,8..2048这些数字拿出来考虑就能够了,其它数字不管怎样都不能參与组成.那么在这些数字基础上,dp[i][j]表示到第i个数字,和为j的情况数,然后对于每一个数枚举取多少个,就能够利用组合数取进行状态转移,这里有一个剪枝,就是假设加超过2048了,那么后面数字的组合数的和所有都是加到2048上面,能够利用公式一步求解,这种整体复杂度就能够满足题目了.然后这题时限卡得紧啊.10W内的逆元不先

LCIS tyvj1071 DP优化

思路: f[i][j]表示n1串第i个与n2串第j个且以j结尾的LCIS长度. 很好想的一个DP. 然后难点是优化.这道题也算是用到了DP优化的一个经典类型吧. 可以这样说,这类DP优化的起因是发现重复计算了很多状态,比如本题k的那层循环. 然后就可以用maxl标记搞一下,将O(n^3)变成O(n^2). #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> usi

Codeforces 413D 2048(dp)

题目连接:Codeforces 413D 2048 题目大意:2048的游戏,两个相同的数x可以变成一个2*x,先给出n,表示在一个1*n的矩阵上面玩2048,规定每次向左移动,并且每次出现一个,给出序列n,表示出现的块的值,0表示既可以是2也可以是4,问说有多少种可能,使得游戏结束后的最大块的值大于等于2^k. 解题思路:dp[i][j][x]表示第i个位置,值为j,x表示先前有没有出现过大于2^k的数: 这种递增的情况可以直接表示为14(总和,以为后面的2,4如果变大,就肯定能和8想合在一起

loj6171/bzoj4899 记忆的轮廊(期望dp+优化)

题目: https://loj.ac/problem/6171 分析: 设dp[i][j]表示从第i个点出发(正确节点),还可以有j个存档点(在i点使用一个存档机会),走到终点n的期望步数 那么 a[i][k]表示i点为存档点,从i点走到k点(正确节点)的期望步数(中间没有其它存档点) 那么a[i][j]可以递推预处理出 其中g[v]表示从一个错误节点v开始走,期望走g[v]步会读档 解方程可以解出 s[j-1]就是点j-1出去的所有错误儿子的g[v]之和 那么接下来只要知道如何求g[v]就行了

poj1088 滑雪(dfs、dp优化)

#include <iostream> #include <map> #include <string> #include <cstdio> #include <sstream> #include <cstring> #include <vector> #include <cmath> #define N 110 int a,b,step=0; int anw=0; int moun[N][N]; int dp

dp优化1——sgq(单调队列)

该文是对dp的提高(并非是dp入门,dp入门者请先参考其他文章) 有时候dp的复杂度也有点大...会被卡. 这几次blog大多数会讲dp优化. 回归noip2017PJT4.(题目可以自己去百度).就是个很好的案例.那题是个二分套dp如果dp不优化复杂度O(n^2logn)还能拿60分(CCF太仁慈了,如果是我直接给10分). 正解加上个单调队列(其实是sliding window)O(nlogn) 我们发现,此类dp是这样的 状态i是由[l,r]转移过来的.且i在向右移动的过程中,[l,r]一