• 51nod 1406 与查询 dp 考虑每一位 避免重复


    题目链接:

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1406

    题意:

    有n个整数。输出他之中和x相与之后结果为x的有多少个。x从0到1,000,000

    思路:

    http://blog.csdn.net/bahuia/article/details/55653495

    这题dp的思路很巧妙,分析一下题目,很容易想到,若已经求出一个数x的合法结果数,那么对于x位数上的子集来说,x的结果对他们也同样适用,例如,能和x=10110进行与运算能得到x的数,和x的子集y=10100进行与运算也一定能得到y。这样就能有一个大概的思路,设dp[x]为n个数中和x进行与运算能得到x的数的个数,dp[x] = sum(dp[z]),其中x是z的子集,但是这样的思路并不完全正确,因为计算sum(dp[z])会包含大量的重复。
    举个例子,x=10100,那么z就有11100,10110,11110等等,注意到在计算dp[11100]的时候就已经算过了dp[11110],这里算x的时候又同时算了dp[11100]和dp[11110],就出现了重复。所以这里不妨每次只用比x多一位为1的数当作z,如dp[10100] = dp[11100] + dp[10110] + dp[10101]。
    但是这样的结果还是存在重复,上面dp[11100]和dp[10110]在计算的时候都会算上dp[11110],dp[11110]就重复算了两次。
    关键就在这一步,回忆类似背包的思想,按位来考虑,设dp[j][i]表示当前处理到第j位的时候,和i进行与运算能得到i的个数,那么dp[j][i] = sum(dp[j-1][k]),其中i是k的子集。这样处理就不会产生上述的重复。
    当然j这一维在处理的时候可以省略,那么方程就是dp[i] = sum(dp[k]),复杂度O(nlogn)
     

    还有一种方法。先将每个a和x分为前k位和后20-k位两部分,A0表示某个a的前k位,A1表示后20-k位,同理有X0X1 
    设状态: 

    dp[x][k]A0&X0A1=X1a

    转移方程: 
    dp[x][k]= dp[x][k1]+dp[x+2^k][k1]xk0
        dp[x][k1]xk1

    显然dp[x][20]=f(x)
     
     
    真难!!! 还是不理解

    代码:

    代码一:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define MS(a) memset(a,0,sizeof(a))
    #define MP make_pair
    #define PB push_back
    const int INF = 0x3f3f3f3f;
    const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
    inline ll read(){
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void write(int x)
    {
        int a1=0,a[20];
        a[1]=0;
        if (!x) a1++;
        while (x)
        {
            a[++a1]=x%10;x/=10;
        }
        for (int i=a1;i>=1;i--)
            putchar(a[i]+'0');
    }
    //////////////////////////////////////////////////////////////////////////
    const int maxn = 1e6+1;
    
    int n,dp[maxn];
    
    int main(){
        n = read();
        int mx = 0;
        for(int i=1; i<=n; i++){
            int x = read();
            dp[x]++;
            mx = max(mx,x);
        }
        int bit = 0;
        while(mx){
            mx >>= 1;
            bit++;
        }
    
        for(int j=0; j<bit; j++){
            for(int i=1; i<maxn; i++)
                if(i & (1<<j))
                    dp[i-(1<<j)] += dp[i];
        }
        dp[0] = n;
        for(int i=0; i<maxn; i++){
            write(dp[i]);
            puts("");
        }
    
    
        return 0;
    }

    代码二:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define MS(a) memset(a,0,sizeof(a))
    #define MP make_pair
    #define PB push_back
    const int INF = 0x3f3f3f3f;
    const ll INFLL = 0x3f3f3f3f3f3f3f3fLL;
    inline ll read(){
        ll x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void write(int x)
    {
        int a1=0,a[20];
        a[1]=0;
        if (!x) a1++;
        while (x)
        {
            a[++a1]=x%10;x/=10;
        }
        for (int i=a1;i>=1;i--)
            putchar(a[i]+'0');
    }
    //////////////////////////////////////////////////////////////////////////
    const int maxn = 1e6+1;
    
    int n,dp[(1<<21)];
    
    int main(){
        n = read();
        for(int i=1; i<=n; i++){
            int x = read();
            dp[x]++;
        }
        
        // for(int j=1; j<21; j++){
        //     for(int i=0; i<maxn; i++){
        //         if(i & (1<<(j-1))){
        //             dp[i] = dp[i][j-1];
        //         }else{
        //             dp[i][j] = dp[i][j-1]+dp[i+(1<<(j-1))][j-1];
        //         }
        //     }
        // }
        // 滚动优化,否则RE    
        for(int j=1; j<21; j++){
            for(int i=0; i<maxn; i++){
                if(i & (1<<(j-1))){
                    dp[i] = dp[i];
                }else{
                    dp[i] = dp[i]+dp[i+(1<<(j-1))];
                }
            }
        }
        
        for(int i=0; i<maxn; i++){
            write(dp[i]);
            puts("");
        }
    
    
        return 0;
    }
  • 相关阅读:
    Codeforces Round #551 (Div. 2) 题解
    【BZOJ5496】[十二省联考2019]字符串问题(后缀树)
    省选题记录
    【BZOJ5495】[十二省联考2019]异或粽子(主席树,贪心)
    Codeforces Global Round 2
    Android Studio安装使用图文教程(转)
    JAVA基础学习day15--集合二 TreeSet和泛型
    AndroidDevTools下载地址
    JAVA基础学习day14--集合一
    JAVA----编程列出一个字符串的全字符组合情况,原始字符串中没有重复字符
  • 原文地址:https://www.cnblogs.com/yxg123123/p/7251206.html
Copyright © 2020-2023  润新知