[牛客练习赛68]牛牛的粉丝(矩阵快速幂之循环矩阵优化)

题目链接
留坑,明天写。

题意:

有个 n n n个点的环,每个点上有一个初始权值 x i x_{i} xi

定义每一轮调整的描述是:对于环上每一个点的权值,有 p 1 p_1 p1的概率流向顺时针方向的下一个点,有 p 2 p_2 p2的概率流向逆时针方向的下一个点,有 p 3 p_3 p3的概率停在原地。(其中 p 1 + p 2 + p 3 = 1 p_{1}+p_{2}+p_{3}=1 p1+p2+p3=1

k k k轮调整之后,每个点的权值期望,答案取模998244353。
3 ≤ n ≤ 500 , 0 ≤ k ≤ 1 0 18 3\leq n\leq 500 ,0\leq k\leq 10^{18} 3n500,0k1018

思路:

首先可以给出每个点进行一轮调整后的值 X i = p 1 x i − 1 + p 3 x i + p 2 x i + 1 X_{i}=p_{1}x_{i-1}+p_{3}x_{i}+p_{2}x_{i+1} Xi=p1xi1+p3xi+p2xi+1

就可以轻松得到解向量的转移一次的方程:
[ p 3 p 2 0 0 p 1 p 1 p 3 p 2 0 0 0 p 1 p 3 p 2 0 0 0 p 1 p 3 p 2 p 2 0 0 p 1 p 3 ] [ x 1 x 2 x 3 x 4 x 5 ] = [ X 1 X 2 X 3 X 4 X 5 ] \left[ \begin{matrix} p_{3} & p_{2} & 0 & 0 & p_{1}\\ p_{1} & p_{3} & p_{2} & 0 & 0 \\ 0 & p_{1} & p_{3} & p_{2} & 0 \\ 0 & 0 & p_{1} & p_{3} & p_{2} \\ p_{2} & 0 & 0 & p_{1} & p_{3} \end{matrix} \right] \left[ \begin{matrix} x_{1} \\ x_{2} \\ x_{3} \\ x_{4} \\ x_{5} \end{matrix} \right] =\left[ \begin{matrix} X_{1} \\ X_{2} \\ X_{3} \\ X_{4} \\ X_{5} \end{matrix} \right] p3p100p2p2p3p1000p2p3p1000p2p3p1p100p2p3x1x2x3x4x5=X1X2X3X4X5

那么我们的答案就是:
[ X 1 X 2 X 3 X 4 X 5 ] = [ p 3 p 2 0 0 p 1 p 1 p 3 p 2 0 0 0 p 1 p 3 p 2 0 0 0 p 1 p 3 p 2 p 2 0 0 p 1 p 3 ] k [ x 1 x 2 x 3 x 4 x 5 ] \left[ \begin{matrix} X_{1} \\ X_{2} \\ X_{3} \\ X_{4} \\ X_{5} \end{matrix} \right] =\left[ \begin{matrix} p_{3} & p_{2} & 0 & 0 & p_{1}\\ p_{1} & p_{3} & p_{2} & 0 & 0 \\ 0 & p_{1} & p_{3} & p_{2} & 0 \\ 0 & 0 & p_{1} & p_{3} & p_{2} \\ p_{2} & 0 & 0 & p_{1} & p_{3} \end{matrix} \right]^{k} \left[ \begin{matrix} x_{1} \\ x_{2} \\ x_{3} \\ x_{4} \\ x_{5} \end{matrix} \right] X1X2X3X4X5=p3p100p2p2p3p1000p2p3p1000p2p3p1p100p2p3kx1x2x3x4x5
现在,它就成了一道矩阵快速幂板题了,但是 500 ∗ 500 ∗ 500 ∗ l o g 1 0 18 ≈ 8 e 9 500*500*500*log10^{18}\approx 8e9 500500500log10188e9,时限1 s s s是过不去的。

那么就要考虑优化。
然后就涨姿势了,还有循环矩阵这么个东西。对于循环矩阵的快速幂而言,是可以优化到 o ( n 2 l o g k ) o(n^{2}logk) o(n2logk)的。

芝士:

循环矩阵:

当一个矩阵的每一行都由前一行右移一列(最后一列移到第一列)得到,我们称这样的矩阵为循环矩阵

性质:

循 环 矩 阵 + 循 环 矩 阵 = 循 环 矩 阵 循环矩阵+循环矩阵=循环矩阵 +=
循 环 矩 阵 × 循 环 矩 阵 = 循 环 矩 阵 循环矩阵\times 循环矩阵=循环矩阵 ×=

用途:

一、根据循环矩阵的定义和性质,我们要存储一个循环矩阵,只要存它的第一行就行了,后面的行可以由第一行得到。
二、我们要计算得到一个循环矩阵,也只要算出它第一行是什么就行了,同样的,后面的行可以通过第一行得到。
三、而循环矩阵通过线性运算仍然为循环矩阵,那么我们思考算循环矩阵乘循环矩阵的复杂度,因为我们只要算第一行就行了,所以矩阵乘法从原来的 o ( n 3 ) o(n^{3}) o(n3)变成了 o ( n 2 ) o(n^{2}) o(n2)

然后,我们发现这题的矩阵就是循环矩阵,所以就可以优化到 o ( n 2 l o g k ) o(n^{2}logk) o(n2logk)了。

代码:

留坑,明天再敲

#include
using namespace std;
typedef long long ll;
const ll mod=998244353;

ll fpow(ll a,ll n)
{
     
    ll sum=1,base=a;
    while(n!=0)
    {
     
        if(n%2)sum=sum*base%mod;
        base=base*base%mod;
        n/=2;
    }
    return sum;
}
ll inv(ll a)
{
     
    return fpow(a,mod-2);
}

struct Cmatrix
{
     
    ll mat[505];
}A;
ll len;
Cmatrix mul(Cmatrix A,Cmatrix B)
{
     
    Cmatrix tmp;
    for(ll i=0;i<len;i++)
    {
     
        tmp.mat[i]=0;
        for(int j=0;j<len;j++)
        {
     
            tmp.mat[i]=(tmp.mat[i]+A.mat[j]*B.mat[(len-j+i)%len])%mod;
        }
    }
    return tmp;
}
Cmatrix ffpow(Cmatrix A,ll n)
{
     
    Cmatrix sum;
    for(ll i=0;i<len;i++)sum.mat[i]=0;
    sum.mat[0]=1;
    while(n!=0)
    {
     
        if(n%2)sum=mul(sum,A);
        A=mul(A,A);
        n/=2;
    }
    return sum;
}

ll x[505],X[505];
int main()
{
     
    ll n,k;
    scanf("%lld%lld",&n,&k);
    len=n;
    ll a,b,c;
    scanf("%lld%lld%lld",&a,&b,&c);
    ll p1=a*inv(a+b+c)%mod;
    ll p2=b*inv(a+b+c)%mod;
    ll p3=c*inv(a+b+c)%mod;
    A.mat[0]=p3;A.mat[len-1]=p1;A.mat[1]=p2;
    A=ffpow(A,k);
    for(ll i=0;i<len;i++)scanf("%lld",&x[i]);
    for(ll i=0;i<len;i++)
    {
     
        X[i]=0;
        for(ll j=0;j<len;j++)
        {
     
            X[i]=(X[i]+x[j]*A.mat[(len-i+j)%len])%mod;
        }
    }
    for(ll i=0;i<len;i++)printf("%lld ",X[i]);
    putchar('\n');
    return 0;
}

你可能感兴趣的:(数学)