BZOJ 2004 [Hnoi2010]Bus 公交线路

题目链接

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

题解

状压dp,记f[i][S]f[i][S]f[i][S]表示[1,i−p][1,i-p][1,ip]的车都被安排好了,而[i−p+1,i][i-p+1,i][ip+1,i]的车中,SSS中有111的位置都安排有车停,并且恰好只有kkk个位置安排了(就是kkk辆车安排到的最后一个站,按照定义,显然kkk辆车安排的终点站必定在[i−p+1,i][i-p+1,i][ip+1,i]内),其他的都没有安排。为了防止重复统计,钦定SSS的最高位(i−p+1i-p+1ip+1)必定为111

转移的话就是,如果f[i][S]f[i][S]f[i][S]f[i−1][T]f[i-1][T]f[i1][T]TTT中的所有111SSS中的所有111能对应上,那么f[i][S]f[i][S]f[i][S]就可以从f[i−1][T]f[i-1][T]f[i1][T]转移过来。

由于状态总数为(nn/2)\binom{n}{n/2}(n/2n)(最大是126),因此可以用矩阵乘法加速转移。

代码

#include <cstdio>
#include <cstring>
 
int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}
 
const int maxm=126;
const int mod=30031;
 
struct matrix
{
  int n,m,a[maxm+2][maxm+2];
 
  matrix operator *(const matrix &other) const
  {
    matrix res;
    res.n=n;
    res.m=other.m;
    memset(res.a,0,sizeof res.a);
    for(int i=1; i<=n; ++i)
      {
        for(int j=1; j<=other.m; ++j)
          {
            for(int k=1; k<=m; ++k)
              {
                res.a[i][j]=(res.a[i][j]+1ll*a[i][k]*other.a[k][j])%mod;
              }
          }
      }
    return res;
  }
};
 
int stand[maxm+2],n,p,k,tot;
matrix start,trans,ans;
 
matrix quickpow(matrix res,matrix a,int b)
{
  while(b)
    {
      if(b&1)
        {
          res=res*a;
        }
      a=a*a;
      b>>=1;
    }
  return res;
}
 
int main()
{
  n=read();
  p=read();
  k=read();
  for(int i=1<<(k-1); i<1<<k; ++i)
    {
      int cnt=0;
      for(int j=0; j<k; ++j)
        {
          if(i&(1<<j))
            {
              ++cnt;
            }
        }
      if(cnt==p)
        {
          stand[++tot]=i;
        }
    }
  start.n=1;
  start.m=trans.n=trans.m=tot;
  for(int i=1; i<=tot; ++i)
    {
      for(int j=1; j<=tot; ++j)
        {
          int r=(stand[i]<<1)&((1<<k)-1),q=stand[j];
          for(int s=0; s<k; ++s)
            {
              if(((r&(1<<s))==0)&&((r|(1<<s))==q))
                {
                  trans.a[j][i]=1;
                }
            }
        }
    }
  start.a[1][tot]=1;
  ans=quickpow(start,trans,n-p);
  printf("%d\n",ans.a[1][tot]);
  return 0;
}

转载于:https://www.cnblogs.com/Canopus-wym/p/10376137.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值