排队 矩阵快速幂优化dp

\(T1\) 排队

?

Description

??

抢饭是高中生活的一部分,现在有一列队伍长度为 \(n\),(注意:由于人与人之间要保持距离,且不同情况所保持的距离大小不同,所以长度并不能直接体现队列的人数)。已知男男之间的距离为 \(a\),男女之间距离为 bb,女女之间距离为 \(c\)。一个男生打饭时间为 \(d\),一个女生打饭时间为 \(e\),求所有情况的排队时间总和(忽略身体的大小对队伍长度的贡献),答案对 $10^{9}+7 $取模。

??

Input Format

一行六个整数 \(n\), \(a\), \(b\), \(c\), \(d\), \(e\)。

?

Output

一行一个整数,即答案。

?

Solution

?

首先我们很容易想到一个DP做法,这里我们构造 \(f\) \([i][0/1]\)数组,表示最后一个人是0/1的情况下,最后一个人打到饭的时间之和,0表示最后一个人是男生,1是女生。

?

定义g\([i][0/1]\)表示最后一个人是 的情况下的总方案数。手推得状态转移方程:

?

?

但我们会发现\(n\)非常的大,但是\(a\),\(b\),\(c\),却很小,因此每次转移时所需要的\(i-a\),\(i-b\),\(i-c\)非常靠近 ,因此可以考虑使用滚动数组转移。

?

但滚动数组并没有对时间上做出优化。

?

这里的转移我们用矩阵乘法来代替,只需构造一个\(4*max(a,b,c)\)阶的转移矩阵即可。

下面我们来详细说一说这个矩阵的构造!!

首先看到\(g\)数组对\(f\)数组的转移造成了影响,所以我们在矩阵中不仅只转移\(f\),还必须加入\(g\)的转移。

按照常理,构造出的初始矩阵长这样:

但考虑到 \(f\) \([i][0/1]\)和g\([i][0/1]\)这四个必须一起转移的东东,所以每个\(i\)要维护四个值,所以在每一个块中维护四个值,所以变成了:

好了,这样就可以解释为什么是\(4*max(a,b,c)\)阶了, 阶就是矩阵长宽的意思啦

然后的矩阵构造就为了一个目标:

这个的意思就是从\(r\)推出\(r+1\),然后对着这个和上面的dp转移方程构造一个转移矩阵就好了.

形如:

构造完成后就将转移矩阵快速幂,快速得到最后的答案 \(f[n][0]\)+\(f[n][1]\)

细节:代码实现时因为是每四个当成一块,所以我们用 \(<<2\)(等价于乘4)也可以迅速得到每个i对应矩阵中的位置,每一块中的选择则用 |(或操作=加操作)来处理,使代码简洁。

#include<bits/stdc++.h>
#define ll long long
#define N 150
using namespace std;
const int mod=1e9+7;
ll n;
int a,b,c,d,e,O;
int g[N][2],f[N][2];// f[i][0/1]表示长度为i的队伍最后为男生(0)或女生(1)的答案  g[i][1/0]与f一样,不过是记录到达这个状态的方案数
struct Matrix{
    int a[N][N];
    Matrix(){memset(a,0,sizeof(a));}
}T,qs,asd;//T为转移矩阵 qs为初始矩阵
void mul(Matrix &CC,Matrix A,Matrix B)
{
    Matrix C;
    int i,j;
    for(i=0;i<O;i++)
       for(j=0;j<O;j++)
          C.a[i][j]=0;

    for(i=0;i<O;i++)   //两个矩阵的相乘
    for(j=0;j<O;j++)
    if(A.a[i][j])
    {
        for(int k=0;k<O;k++)
           if(B.a[j][k])
            C.a[i][k]=(1ll*A.a[i][j]*B.a[j][k]+C.a[i][k])%mod;
    }
    CC=C;
}
void Ksm(Matrix &CC,Matrix AA,ll t)  //标准快速幂
{
    Matrix A,C;
    t--;
    A=C=AA;
    while(t)
    {
        if(t&1)
        {
            mul(C,C,A);
        }
        mul(A,A,A);
        t>>=1;
    }
    CC=C;
}
int main()
{
    int i,j;
    scanf("%lld",&n);
    scanf("%d%d%d%d%d",&a,&b,&c,&d,&e);
    g[0][0]=g[0][1]=1;  //初始化
    f[0][0]=d,f[0][1]=e;   //初始化
    int o=max(a,max(b,c)); //转移所需的最小的矩阵
    O=o<<2;//矩阵长宽
    for(i=1;i<o;i++)// 初始化第一个矩阵
    {
        if(i>=a) (f[i][0]+=f[i-a][0])%mod,(g[i][0]+=g[i-a][0])%mod;
        if(i>=b) (f[i][0]+=f[i-b][1])%mod,(g[i][0]+=g[i-b][1])%mod,(f[i][1]+=f[i-b][0])%mod,(g[i][1]+=g[i-b][0])%mod;
        if(i>=c) (f[i][1]+=f[i-c][1])%mod,(g[i][1]+=g[i-c][1])%mod;
        f[i][0]=(1ll*g[i][0]*d+f[i][0])%mod;
        f[i][1]=(1ll*g[i][1]*e+f[i][1])%mod;
    }
    //下面开始把我们构造的转移矩阵完善一下
    for(i=1;i<o;i++)
    {
        for(j=0;j<4;j++)
        T.a[i<<2|j][(i-1)<<2|j]=1; //把矩阵中的1全填了再说
    }
    T.a[(o-b)<<2][(o-1)<<2|1] = T.a[(o-b)<<2|2][(o-1)<<2|3] = T.a[(o-b)<<2|1][(o-1)<<2] = T.a[(o-b)<<2|3][(o-1)<<2|2] = 1; //把公式中的全部i-b的影响填上1
    T.a[(o-b)<<2|2][(o-1)<<2|1] = e; T.a[(o-b)<<2|3][(o-1)<<2] = d; //把e和d填上
     //后面也在一个一个填数
     ++T.a[(o-a)<<2][(o-1)<<2]; ++T.a[(o-a)<<2|2][(o-1)<<2|2];
     (T.a[(o-a)<<2|2][(o-1)<<2]+=d)%mod;
     ++T.a[(o-c)<<2|1][(o-1)<<2|1]; ++T.a[(o-c)<<2|3][(o-1)<<2|3];
     (T.a[(o-c)<<2|3][(o-1)<<2|1]+=e)%mod;

     for(i=0;i<o;i++)  //把之前求出的初始f付给这个初始矩阵
     {
        qs.a[0][i<<2] = f[i][0];
        qs.a[0][i<<2|1] = f[i][1];
        qs.a[0][i<<2|2] = g[i][0];
        qs.a[0][i<<2|3] = g[i][1];
     }
     Ksm(T,T,n-o+1); mul(qs,qs,T);  //快速转移
     printf("%d\n",(qs.a[0][(o-1)<<2]+qs.a[0][(o-1)<<2|1])%mod);  //取最后的答案
    return 0;
} 

原文地址:https://www.cnblogs.com/yzxx/p/11722839.html

时间: 2024-11-06 11:40:19

排队 矩阵快速幂优化dp的相关文章

矩阵快速幂 优化dp 模板

相关博客 :https://blog.csdn.net/china_xyc/article/details/89819376#commentBox 关于能用矩阵乘法优化的DP题目,有如下几个要求: 转移式只有加法,清零,减法etc.,max和min运算不允许 转移式中关于前几位dp结果得到的系数必须是常量 转移次数一般超级多 由于转移次数多,一般都要模一个int范围内的数 综上,举一个例子: dp[i]=a×dp[i−1]+b×dp[i−2]+c×dp[i−3] 其中,a,b,c是常量,而在需要

bzoj 4000 矩阵快速幂优化DP

建立矩阵,跑快速幂 1 /************************************************************** 2 Problem: 4000 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:32 ms 7 Memory:836 kb 8 ****************************************************************/ 9 10 #inclu

LibreOJ #2325. 「清华集训 2017」小Y和恐怖的奴隶主(矩阵快速幂优化DP)

哇这题剧毒,卡了好久常数才过T_T 设$f(i,s)$为到第$i$轮攻击,怪物状态为$s$时对boss的期望伤害,$sum$为状态$s$所表示的怪物个数,得到朴素的DP方程$f(i,s)=\sum \frac{1}{sum+1}*(f(i+1,s')+[s==s'])$ 状态数只有$C_{8+3}^3=165$个,所以就可以矩乘优化啦.再加上一个用于转移的$1$,矩阵大小是$166*166$的,因为多组询问,所以可以先把$2$的所有次幂的矩阵都预处理出来. 然后会发现复杂度是$O(T*166^3

czy的后宫——矩阵快速幂优化DP

题意 有 n 个位置排成一行,可以放 m 种妹子.每个位置可以放也可以不放,规定某些妹子不能相邻,求方案数. 分析 #include<bits/stdc++.h> using namespace std; typedef long long ll; ll qmul(ll x,ll y,ll p){ //快速乘 x%=p; y%=p; ll ans=0; while(y){ if(y&1){ ans+=x; if(ans>=p) ans-=p; //这样写不能有负数 } x<

hdu 5411 CRB and Puzzle (矩阵快速幂优化dp)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5411 题意:按题目转化的意思是,给定N和M,再给出一些边(u,v)表示u和v是连通的,问走0,1,2.....M步的方案数. 分析:这题和 hdu5318 The Goddess Of The Moon差不多,就是多了一个等比数列求和. 代码: #include <cstdio> #include <iostream> #include <cstring> using name

poj 3744 概率dp 矩阵快速幂优化

一位童子兵要穿过一条路,路上有些地方放着地雷.这位童子兵非常好玩,走路一蹦一跳的.每次他在 i 位置有 p 的概率走一步到 i+1 ,或者 (1-p) 的概率跳一步到 i+2.童子兵初始在1位置,求他安全通过这条道路的概率. 以所在位置为状态,dp[i] 表示在位置 i 的安全的概率. dp[i] = p * dp[i-1] + (1 - p) * dp[i-2]; // i 位置没有地雷 但是题目数据的范围是 10^8 这样dp的话会 TLE. 想想可以用矩阵快速幂优化.简单退出矩阵是 |p

HDU 5863 cjj&#39;s string game ( 16年多校10 G 题、矩阵快速幂优化线性递推DP )

题目链接 题意 : 有种不同的字符,每种字符有无限个,要求用这k种字符构造两个长度为n的字符串a和b,使得a串和b串的最长公共部分长度恰为m,问方案数 分析 : 直觉是DP 不过当时看到 n 很大.但是 m 很小的时候 发现此题DP并不合适.于是想可能是某种组合数学的问题可以直接公式算 看到题解的我.恍然大悟.对于这种数据.可以考虑一下矩阵快速幂优化的DP 首先要想到线性递推的 DP 式子 最直观的想法就是 dp[i][j] = 到第 i 个位置为止.前面最长匹配长度为 j 的方案数 但是如果仔

矩阵快速幂优化递推总结

RT,主要总结一下矩阵的求法. 首先能用矩阵快速幂优化的递推类型是f[n]=5f[n-3]+6f[n-2]+2f[n-1]+n^2+n+8之类的 也就是说递推是线性递推且f[n-i]前面的系数是常数,可以含有与n有关的多项式,也可以含有常数的这种递推,下面总结一下矩阵的写法: 先考虑最简单的常数,我们其实可以忽略常数,因为顶多在没有常数的矩阵外面加一行一列就行了 以f[n]=2f[n-1]+6f[n-2]+5f[n-3]+n^2+n为例 先写迭代的矩阵,一般可以写成一行,右边有几项写几项 {f[

poj3744之矩阵快速幂+概率DP

Scout YYF I Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4410   Accepted: 1151 Description YYF is a couragous scout. Now he is on a dangerous mission which is to penetrate into the enemy's base. After overcoming a series difficulties,