zoukankan      html  css  js  c++  java
  • bzoj千题计划247:bzoj4903: [Ctsc2017]吉夫特

    http://uoj.ac/problem/300

    预备知识:

    C(n,m)是奇数的充要条件是 n&m==m

    由卢卡斯定理可以推出

    选出的任意相邻两个数a,b 的组合数计算C(a,b)必须是奇数

    所以可以设dp[i][j] 表示前i个数里面,选的最后一个数是第j个数的方案数

    转移的时候,枚举前i-1个数选的最后一个数k,

    若C(k,i)是奇数,dp[i][j]+=dp[i-1][k]

    时间复杂度:O(n^3)

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 20
    
    const int mod=1e9+7;
    
    int a[N];
    
    int dp[N][N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    bool judge(int x,int y)
    {
        if(!x) return true;
        return (x&y)==y;
    }    
    
    int main()
    {
        int n;
        read(n);
        for(int i=1;i<=n;++i) read(a[i]);
        dp[0][0]=1;
        for(int i=1;i<=n;++i)
            for(int j=1;j<=i;++j) 
            {
                dp[i][j]=1;
                for(int k=1;k<j;++k)
                    if(judge(a[k],a[j])) 
                    {
                        dp[i][j]+=dp[i-1][k];
                        dp[i][j]-=dp[i][j]>=mod ? mod : 0;
                    }
            }
        int ans=0;
        for(int i=1;i<=n;++i) 
        {
            ans+=dp[n][i]-1;
            ans-=ans>=mod ? mod : 0;
        }
        printf("%d",ans);
    }        
    View Code

    优化:

    dp[i] 表示选的最后一个数是第i个数的方案数

    枚举前面的i-1个数,

    若C(a[i],a[j])是奇数,dp[i]+=dp[j]

    时间复杂度:O(n^2)

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 2018
    
    const int mod=1e9+7;
    
    int a[N];
    
    int dp[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int main()
    {
        int n;
        read(n);
        for(int i=1;i<=n;++i) read(a[i]);
        for(int i=1;i<=n;++i) dp[i]=1;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<i;++j) 
                if((a[j]&a[i])==a[i]) dp[i]+=dp[j];
        }
        int ans=0;
        for(int i=1;i<=n;++i) 
        {
            ans+=dp[i]-1;
            ans-=ans>=mod ? mod : 0;
        }
        printf("%d",ans);
    }        
    View Code

    再优化:

    dp[i] 表示选的最后一个数是i的方案数

    dp[i] 能转移到i的子集,

    所以枚举子集j,若j在i的后面,那么dp[j]+=dp[i]

    时间复杂度:O(3^(logn))

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 233334
    
    const int mod=1e9+7;
    
    int a[N];
    
    int dp[N];
    int pos[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int main()
    {
        int n;
        read(n);
        for(int i=1;i<=n;++i) read(a[i]),pos[a[i]]=i;
        int bit,sum;
        for(int i=1;i<=n;++i)
        {
            dp[a[i]]++;
            for(int j=(a[i]-1)&a[i];j;j=(j-1)&a[i]) 
                if(pos[j]>i)
                {
                    dp[j]+=dp[a[i]];
                    dp[j]-=dp[j]>=mod ? mod : 0;
                }
        }
        int ans=0;
        for(int i=1;i<=n;++i) 
        {
            ans+=dp[a[i]]-1;
            ans-=ans>=mod ? mod : 0;
        }
        printf("%d",ans);
    }        
    View Code

    常数优化:

    边读入边计算,接着累计进答案

    就可以不用判断子集是否在i的后面

    因为在前面的话,前面的已经累积进答案了

    #include<cstdio>
    #include<iostream>
    
    using namespace std;
    
    #define N 233334 
    
    const int mod=1e9+7;
    
    int a[N];
    
    int dp[N];
    
    void read(int &x)
    {
        x=0; char c=getchar();
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
    }
    
    int main()
    {
        int n,x;
        int ans=0; 
        read(n);
        for(int i=1;i<=n;++i) 
        {
            read(x);
            dp[x]++;
            for(int j=(x-1)&x;j;j=(j-1)&x)
            {
                dp[j]+=dp[x];
                dp[j]-=dp[j]>=mod ? mod : 0; 
            }
            ans+=dp[x]-1;
            ans-=ans>=mod ? mod : 0; 
        }
        printf("%d",ans);
    }        
    View Code
  • 相关阅读:
    IE6,IE7,IE8,Firefox 兼容的css hack以及常见兼容性问题
    BAT及各大互联网公司2014前端笔试面试题:HTML/CSS/JAVASCRIPT
    C语言学习
    HDU 3861 The King’s Problem 最小路径覆盖(强连通分量缩点+二分图最大匹配)
    HDU 2089 不要62【解题报告】
    POJ2762 Going from u to v or from v to u? 强连通分量缩点+拓扑排序
    HDU 1754 线段树入门解题报告
    2019/4/22 kmp模板
    2019/4/22 拓扑排序的高效写法. 模板题HDU1285:确定比赛名次
    HDU 2544最短路 【dijkstra 链式前向星+优先队列优化】
  • 原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/8469256.html
Copyright © 2011-2022 走看看