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();
}
最后,在这里说一句:

浙公网安备 33010602011771号