bzoj2004公交线路

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2004

好美妙的矩阵乘。

思考:

0.在一个序列上。所以考虑dp。

1.p<=10,k<=8,所以考虑状压,1表示这一站正有公交车。

2.n<=1e9,考虑矩阵乘优化dp。

3.因为每一步<=p,所以维护长度为p的区间中的当前状态就行了,其他位置肯定全是0。这样也压得下。

实现:

为了不重不漏,需要指定一个顺序一样的东西。

  比如我们可以指定最左边一定得有一辆车,而且每次状态改变一定是这辆车走了。

    转移条件就是(当前状态)<<1 与 (目标状态) 只有一位不同,即( (当前状态)<<1 & (目标状态) )==k-1。

  对称地,我们也可以指定最右边一定得有一辆车,状态改变时是随便位置的一辆车走到了这个位置。

    转移条件略。

  因为只能从左往右走,所以这两种方式比指定中间某个位置一定得有一辆车更优。

更妙的是因为每次移动区间之前都有一个 不与之前重复的 指定位置一定有车,而本次更新出来的状态都是建立在该位置有车的前提上的,所以就能保证每一站都被停过!

每一站都只停一次就是通过区间内有k个1来保证。

最后需要乘一下最初的状态,就是开头有k个1的那个。

  如果我们把开始的右端看作第k个位置,结束就是在第n个位置;而且这样开始和结束的状态就都是000001111111的样子。

  如果从1~(1<<p-1)枚举数字是否合法,则第一个得到的合法状态就是000001111111的样子。

  所以最后乘一个只有[1][1]是1的状态矩阵(初始),再输出[1][1]位置的值(最终)就行啦!因为乘状态矩阵对[1][1]的值无影响,所以不乘也行。

代码:

  合法状态不用算末位是0但有k个1的,因为没人转移给它们值,所以它们就没用了。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=155,mod=30031;
int n,m,p,cnt,lm,list[N];
struct Matrix{
    int v[N][N];
    void init()
    {
        for(int i=1;i<=cnt;i++)v[i][i]=1;
    }
    Matrix operator *(const Matrix &b)const
    {
        Matrix tp;memset(tp.v,0,sizeof tp.v);
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                for(int k=1;k<=cnt;k++)
                    (tp.v[i][j]+=v[i][k]*b.v[k][j])%=mod;
        return tp;
    }
}res,tp;
int num(int x)//很好 
{
    int ct=0;
    while(x){x-=(x&-x);ct++;}
    return ct;
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    lm=(1<<p);
    for(int i=1;i<lm;i++)
        if((i&1)&&num(i)==m)list[++cnt]=i;
    for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                if(num(list[i]&(list[j]<<1))==m-1)tp.v[i][j]=1;
    int ct=n-m;res.init();
    while(ct)
    {
        if(ct&1)res=res*tp;
        tp=tp*tp;ct>>=1;
    }
    printf("%d",res.v[1][1]);
    return 0;
}

 

posted on 2018-05-15 23:51  Narh  阅读(558)  评论(0编辑  收藏  举报

导航