P4599 题解

这题,不好评价,能评黑感觉一半靠得是写的依托勾石的题面。

清楚地表述题意

\(m\) 种颜色,定义 \(f_i\)\(2\times i\) 的网格中本质不同且满足四联通的网格颜色不相同的填色方案计数。方案不同当且仅当存在格点颜色不同。

求:\(\sum_{i=1}^{n}(2\times i)^m\times f_i\),对 \(p\) 取模。

分析

延续上面的定义,若已知 \(f_{i-1}\) 如何求出 \(f_i\)。我们要在最后一列之后填色。显然有两种情况:

\(1.\) 上面的点颜色与上一列下面的点颜色相同,显然剩下的点可以填除这种颜色以外的所有颜色,贡献是 \((m-1)\times f_{i-1}\)

\(2.\) 上面的点与上一列下面的点颜色不同,又因为该点不能和他左边的点同色,共 \(m-2\) 种,在该前提下,下面的点可填色也是 \(m-2\) 种。贡献是 \((m-2)^2\times f_{i-1}\)

因此有 \(f_i=f_{i-1}\times ((m-2)^2+m-1)=f_{i-1}\times (m^2-3\times m+3)\)

\(k=m^2-3\times m+3\) 显然有 \(f_i=f_1\times k^{i-1}\)\(f_1\) 显然是 \(m\times(m-1)\)

来化简题目给我们的式子

\(\sum_{i=1}^{n}(2\times i)^m\times f_i=2^m\times f_1\times\sum_{i=1}^{n}i^m\times k^{i-1}\)

显然只用求出 \(\sum_{i-1}^n i^m\times k^{i-1}\)

观察数据范围发现应当有两种做法。

题解

  • \(\text{subtask 1}\)

\(m\le 200, p\le 10^9\)

这个式子,看着就很像矩阵,考虑知道前 \(i-1\) 项的什么信息可以推到第 \(i\) 项。

二项式定理可解决 \(i^m\) 所以要维护 \(i^0,i^1\cdots i^m\),发现每次转移刚好给式子乘上一个 \(k\),可以把这个 \(k\) 放到转移里。在这个过程中统计前缀和是方便的。

时间复杂度 \(O(m^3\times \log n)\)。有足足八十分之多。

  • \(\text{subtask 2}\)

\(m,p\le 3000\)\(m^3\) 的一次转移已经 T 飞了(笑)。

发现 \(p\) 很小,要有什么变化也是在这上面的,很典地,有:\((i+p)^m\equiv i^m\)

出现的这些值以 \(p\) 为一个周期出现,结合式子的形式发现:前一项与后一项差 \(k^p\) 倍。也就是说,每 \(p\) 个求和,他是一个等比数列。

就好做了,算出首项,公比已知,算出这部分,把散的加上去,完结撒花。

了吗?看看题,\(p\) 可以不是质数,直接除是错的。

怎么办呢?我们模仿快速幂,\(\sum_{i=1}^{n} o^i=o^{\lfloor \frac{n}{2}\rfloor}\times(\sum_{i=1}^{\lfloor \frac{n}{2}\rfloor}o^i+1)+o^n\times[o\mod 2=1]\)

这个式子是可以递归做的,这部分解决。

复杂度 \(O(p\log n+\log\frac{n}{p}\times\log n)\)

想通了就好写的,但是我才不会告诉你我因为最后忘乘系数挑了一个小时。

#include<iostream>
#include<cstring>
int n, m, p;
inline int qp(int a, int x){ int r = 1;
    for(; x; x >>= 1, a = 1ll * a * a % p)
        x & 1 ? r = 1ll * r * a % p : 0; return r;
}
struct mt{
    int a[205][205];
    mt operator * (mt&x){
        mt r; memset(r.a, 0, sizeof r.a);
        for(int i = 0; i <= m+1; i++)
        for(int k = 0; k <= m+1; k++)
        if(a[i][k]) for(int j=0; j <= m+1; j++)
        if(x.a[k][j]) r.a[i][j]=(r.a[i][j]+1ll*a[i][k]*x.a[k][j]%p)%p;
        return r;
    }
};
inline mt qp(mt a, int x){
    mt r; memset(r.a, 0, sizeof r.a);
    for(int i = 0; i <= m+1; i++) r.a[i][i]=1;
    for(; x; x>>=1, a = a * a)
        x & 1 ? r = r * a : r;
    return r;
}
namespace xjbg1{
    int c[205][205]; mt o, a;
    int main(){
        c[0][0] = 1;    
        for(int i = 1; i <= m; i++){
            c[i][0] = 1;
            for(int j = 1; j <= m; j++)
                c[i][j] = (c[i-1][j] + c[i-1][j-1]) % p;
        }
        int k = (m * m - 3 * m + 3) % p;
        for(int i = 0; i <= m; i++) a.a[i][0] = 1;
        for(int i = 0; i <= m; i++)
            for(int j = 0; j <= i; j++)
            o.a[i][j] = 1ll * k * c[i][j] % p;
        o.a[m+1][m+1] = o.a[m+1][m] = 1;
        a = qp(o, n - 1) * a;
        int rs = (a.a[m+1][0]+a.a[m][0]) % p;
        std::cout << 1ll * rs * qp(2, m) % p * (m - 1) % p * m % p;
        return 0;
    }
}
namespace xjbg2{
    int k;
    inline int sl(int x,int n){
        if(n == 1) return x;
        if(n & 1) return 1ll * (sl(x, n - 1) + 1) * x % p;
        return 1ll * sl(x, n >> 1) * (1 + qp(x, n >> 1)) % p;
    }
    int main(){
        int x = 0; k = (m * m - 3 * m + 3) % p;
        for(int i = 1; i <= p; i++)
            x = (x + 1ll * qp(i, m) % p * qp(k, i - 1)) % p;
        x = 1ll * x * (sl(qp(k, p), n / p - 1) + 1) % p;
        for(int i = n - n % p + 1; i <= n; i++)
            x = (x + 1ll * qp(i, m) % p * qp(k, i - 1)) % p;
        std::cout << 1ll * x * qp(2, m) % p * m % p * (m-1) % p;
        return 0;
    }
}
int main(){
    std::cin >> n >> m >> p;
    m <= 200 ? xjbg1::main() : xjbg2::main();
}

最后,在这里说一句:

\[\textbf{SNOI 2024 RP++} \]

posted @ 2023-12-28 18:43  xlpg0713  阅读(39)  评论(0)    收藏  举报