zoukankan      html  css  js  c++  java
  • HNOI2010 公交线路

    题目链接:戳我

    看到k,p的范围这么小,显然要状压DP啊!
    但是要怎么状压DP呢。
    我们先注意到每p个公交站,这k辆公交车都要至少出现一次。因为答案是按集合算的,所以公交车之间不做区别,换句话说就是我们可以讲题目简化一下——
    1-n的n个元素,k个集合,保证一个元素只出现在一个集合中(不能多余一个也不能少于一个),任意一个集合中的元素从小到大排序之后,两两之间的编号相差不能超过p,问方案数?
    我们设状态(dp[i][j])表示前i个数,从i(包括i)往前p个数的状态,方案数。因为要状压,所以肯定要想出来如何用01表示状态——0表示这个位置不是任何一个集合当前选的最后一个数,1表示是。
    那么就是每p个数中,一定要有k个数是1。
    我们预处理出来合法的状态,然后再处理出哪些状态之间可以互相转移——i可以转移到j,当且仅当((i<<1)&j==k-1)(j因为最后一位必须为1)。这样算起来第二维状态数最多也是(2^{k-1}),不是很大。
    然后就是(dp[i][j]=sum dp[i-1][k]),其中k状态能够转移到j。

    发现n很大,所以要矩阵快速幂。
    具体实现可以看一下代码。
    最后额外解释一下:
    1、为什么矩阵快速幂的次数为n-k?因为开始k个是出发站,所以每一位一定是1。我们从([k+1,k+p])开始,而且它们肯定都能从k个1转移过来。
    2、为什么最后输出的是[1][1]?因为矩阵快速幂里面记录的都是预处理出来的合法状态,而我们处理出来的第一个合法状态是0011...11(一共p位,后k位为1),考虑一下位运算的顺序,这是很显然的。题目中要求的也是最后k为必须为终点站,所以我们直接输出第一个的答案就行了。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 155
    #define mod 30031
    using namespace std;
    int n,k,p,cnt;
    int c[MAXN];
    struct Node{int t[MAXN][MAXN];}a,init;
    inline Node calc(Node x,Node y)
    {
        Node cur;
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                cur.t[i][j]=0;
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                for(int k=1;k<=cnt;k++)
                    cur.t[i][j]=(cur.t[i][j]+x.t[i][k]*y.t[k][j]%mod)%mod;
        return cur;
    }
    inline Node fpow(Node x,int y)
    {
        Node cur;
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                cur.t[i][j]=0;
        for(int i=1;i<=cnt;i++) cur.t[i][i]=1;
        while(y)
        {
            if(y&1) cur=calc(cur,x);
            x=calc(x,x);
            y>>=1;
        }
        return cur;
    }
    inline bool check(int x,int y)//x是否能够转移到y
    {
        int cur_ans=((c[x]<<1)&c[y]),cur_sum=0;
        for(int i=0;i<p;i++)
            if(cur_ans&(1<<i))
                cur_sum++;
        if(cur_sum==k-1) return true;
        else return false;
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        #endif
        scanf("%d%d%d",&n,&k,&p);
        for(int i=0;i<(1<<p);i++)
        {
            int cur_sum=0;
            for(int j=0;j<p;j++)
                if(i&(1<<j))
                    cur_sum++;
            if(cur_sum==k&&(i&1)) c[++cnt]=i;
        }
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                if(check(i,j)&&(c[j]&1))
                    init.t[i][j]=1;
        init=fpow(init,n-k);
        printf("%d
    ",init.t[1][1]);
        return 0;
    }
    
  • 相关阅读:
    MySQL之IDE工具介绍及数据备份(数据库导入,导出)
    jmeter test Fragment
    python创建虚拟环境
    遇到的问题
    文件操作
    六、迭代器与生成器
    五、IO编程
    简单的例子
    四、函数
    三、集合与格式化
  • 原文地址:https://www.cnblogs.com/fengxunling/p/10888538.html
Copyright © 2011-2022 走看看