• 【bzoj3195】【 [Jxoi2012]奇怪的道路】另类压缩的状压dp好题


    这里写图片描述
    (上不了p站我要死了)

    啊啊,其实想清楚了还是挺简单的。

    Description
    小宇从历史书上了解到一个古老的文明。这个文明在各个方面高度发达,交通方面也不例外。考古学家已经知道,这个文明在全盛时期有n座城市,编号为1..n。m条道路连接在这些城市之间,每条道路将两个城市连接起来,使得两地的居民可以方便地来往。一对城市之间可能存在多条道路。
    据史料记载,这个文明的交通网络满足两个奇怪的特征。首先,这个文明崇拜数字K,所以对于任何一条道路,设它连接的两个城市分别为u和v,则必定满足1 <=|u - v| <= K。此外,任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)。不过,由于时间过于久远,具体的交通网络我们已经无法得知了。小宇很好奇这n个城市之间究竟有多少种可能的连接方法,于是她向你求助。
    方法数可能很大,你只需要输出方法数模1000000007后的结果。
    Input
    输入共一行,为3个整数n,m,K。
    Output
    输出1个整数,表示方案数模1000000007后的结果。
    Sample Input
    【输入样例1】
    3 4 1
    【输入样例2】
    4 3 3
    Sample Output
    【输出样例1】
    3
    【输出样例2】
    4
    【数据规模】
    HINT
    100%的数据满足1<= n <= 30, 0 <= m <= 30, 1 <= K <= 8.
    【题目说明】
    两种可能的连接方法不同当且仅当存在一对城市,它们间的道路数在两种方法中不同。
    在交通网络中,有可能存在两个城市无法互相到达。

    题目说要取模,通常会这样的有数论或dp。所以虽然数据范围较小,想到搜索的简直233

    有两维很好想到,及处理到第i个点,建了j条路的方案数。但是这道题的条件很苛刻,不仅距离不能超过k,还要求所有的点都有偶数条边连接。如果只有距离限制,其实还好处理。然而奇偶性该如何处理?
    平时的状压我们认为是选与不选的状态,但是这里算是要开眼界了。只要是两种对立态,就可以用01表示。所以多增一维,记录i与i前面的k个点的奇偶。

    然后怎么转移呢?

    首先,如果要让我们来建路,我们会枚举每一个点,然后向前面的点建边。所以对于f[i][j][s],我们就考虑i向前k个点连边。

    当时我并没有想清楚,去搜题解,于是……

    “第一眼看到题比较裸的状压dp就是f[i][j][s]表示考虑到第i个点,连了j条边,i和i左边k个点奇偶性状态为s的方案数,然后枚举i和谁连边向j+1转移,每个s再向i+1转移。
      写完后发现这个做法有bug,因为同一个状态因为i向外连边的顺序不同而被重复记数了。比如:
    这里写图片描述
    第一个状态就在第四个状态中被重复记了两次,所以我们在加一位状态l,表示i正准备和i-l连边,这样i的边就是从左往右连的,就不会重复记数了。”——SD_le

    于是我想都没想,就多加了一维l

    于是状态转移就变成了(->表示做贡献,这是顺推的方式)
    f[i][j][s][l]->f[i][j][s][l+1]

    f[i][j][s][l]->f[i][j+1][s^(1 << l )^1][l]

    这是该解法的完整代码:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int mod=1000000007;
    
    int N,M,K;
    int f[35][35][1<<10][10];
    
    int main(){
        scanf("%d%d%d",&N,&M,&K);
        f[1][0][0][0]=1;
        for(int i=1;i<=N;i++){
            for(int j=0;j<=M;j++){
                for(int k=0;k<(1<<(K+1));k++){
                    for(int l=min(K,i-1);l>=1;l--){
                        int tmp=f[i][j][k][l];
                        f[i][j][k][l-1]+=tmp%=mod;
                        f[i][j+1][k^(1<<l)^1][l]+=tmp%=mod;
                    }
                    if(((k>>K)&1)==0) f[i+1][j][(k<<1)&((1<<(N+1))-1)][min(K,i)]+=f[i][j][k][0]%=mod;
                }
            }
        }
        printf("%d
    ",f[N][M][0][0]);
        return 0;
    }

    然后手贱看了一下rank,发现doggu神名列前茅(前三名打表)。于是前去请教

    我们发现,其实第4维l是无用的。为什么会出现上文说到的bug呢?因为我们把建边的顺序更改了一下,造成了重复计算,如果我们直接强制规定建边顺序,就可以减少一维。

    被doggu神改的一塌糊涂的三维做法:

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int MOD=1000000007;
    
    int N,M,K;
    int f[2][35][1<<9], cur, pow[9], lim, lm;
    inline void add(int &a,int b) {a=a-MOD+b>0?a-MOD+b:a+b;}
    int main(){
        scanf("%d%d%d",&N,&M,&K);lm=1<<K;lim=1<<(K+1);
        f[cur][0][0]=1;
        pow[0]=1;
        for( int i = 1; i <= K; i++ ) pow[i]=pow[i-1]<<1;
        for(int i=1,l,j,k,st,sta;i<=N;i++){
            cur^=1;memset(f[cur],0,sizeof(f[cur]));
            for( j = 0; j <= M; j++ ) 
                for( st = 0, sta = 0; st < lm; st++, sta+=2 ) 
                    f[cur][j][sta]=f[cur^1][j][st];
            for(l=min(K,i-1);l>=1;l--)
                for(j=0;j<=M;j++)
                    for(k=0;k<lim;k++)
                        add(f[cur][j+1][k^pow[l]^1],f[cur][j][k]);
        }
        printf("%d
    ",f[cur][M][0]);
        return 0;
    }
  • 相关阅读:
    redis requires Ruby version >= 2.2.2.
    redis-持久化、主从复制、集群
    lucene索引文件大小优化小结
    spring+websocket的整合实例--可使用
    使用nexus3.10搭建maven私有仓库
    oracle 查看所有表的数据量并排序
    Spring框架-经典的案例和demo,一些可以直接用于生产,使用atomikos来处理多数据源的一致性事务等
    ORACLE WITH AS 用法
    判断对象部分属性是否为空
    代码行数统计
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763103.html
Copyright © 2020-2023  润新知