DTOI 3523 环线(tour)
来自 FJWC 2018 Day5
以下为题解
对于 30% 的数据, n<=4 0,k<=1000 。计算每个点在第 1 – k-1步到每个点 步到每个点 的方法数 ,将所有自环(回到己)的方案统计起来。 O(n^3*k)
对于 60% 的数据, n<=80 ,k<=10^6 。
计算每个点在第 1 – k-1步到每个点的方法数 ,使用 ,使用 矩阵乘法快速幂 解决。
A[i][j]=1 表示 i,j 之间有无向边 。
设 B矩阵中仅 B[i][i]=1 ,其他为 0(单位矩阵) 。
令 B * A^x = C B * A^x = C B * A^x = C B * A^x = C B * A^x = C,则 C[i][j] 表示 i到 j长度恰为 x的路径数 。将 B*A ,B*A^2 , B*A^3 …… B*A^(k -1) 的主对角线值 (C[i][i ],即自环 )累计即为答案 。
我们可以新建 n个虚拟点 ,不妨 标号为 为 n+0 ,n+1, ..n+ (n-1) 。从点 i连一 条到点 i+n 的边 (i < n ),再给所有的点 n+i 连一条向自己的边。 连一条向自己的边。 这一点 用矩阵 乘法中体现的话 ,即将矩阵扩大为 2n*2n :
A[i,i+n]=1 , n,n] = 1 ( i< n)
令 B * A^k = C B * A^k = C B * A^k = C B * A^k = CB * A^k = CB * A^k = C,则ΣC[i,i+n]C[i,i+n] (i< n) - n 即为答案 即为答案 。即从点 i经过 n步走 到点 i+n 的方案数,减去长度为 0的自环方案数 (n)
时间复杂度 O(logk*(2n)^3)
对于 100% 的数据, n<=100 ,k<=10^6
计算每个点在第 1 – k-1步到每个点的方法数 。
将 B*A ,B*A^2 ,B*A^3 …… B*A^(k -1) 的主对角线值 (C[i][i] ,即自环 )累 计即为答案 。
相当于 D=B * (A + A^2 3 … A^(k -1)) ,求 D的主对角线 (D[i][i]) (D[i][i]) 的 和。
而求解 A + A^2 3 … A^(k -1) ,类似于 求解 等比数列 。
根据 K的奇偶 有,
如果 k为偶数,那么
(A+A^2+….K) = A+…+K/2)+2*(2)
如果 k为奇数,那么
(A+A^2+….K) = A+…+K/2)+2*(k
时间复杂度 O(logk * n^3)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=106;
typedef long long ll;
int n,k,mod,last,lastc,now,nowc,p;
char ch[MAXN][MAXN];
ll ansn,a[20][MAXN][MAXN],b[2][MAXN],c[2][MAXN];
inline void Multi(ll _a[MAXN][MAXN],ll _b[MAXN][MAXN],ll _c[MAXN][MAXN]) {
for (int i=1;i<=n;i++)
for (int k=1;k<=n;k++)
if (_a[i][k]) for (int j=1;j<=n;j++)
if (_b[k][j]) _c[i][j]=(_c[i][j]+_a[i][k]*_b[k][j])%mod;
}
inline void Init(int x) {
last=lastc=0,now=nowc=1;
for (int i=1;i<=n;i++) b[0][i]=c[0][i]=0;
b[0][x]=c[0][x]=1;
}
void fastpow(int x) {
p=0;
while (x) {
if (x&1) {
for (int i=1;i<=n;i++) {
c[nowc][i]=0;
for (int j=1;j<=n;j++) c[nowc][i]=(c[nowc][i]+a[p][i][j]*c[lastc][j])%mod;
c[nowc][i]+=b[last][i];
if (c[nowc][i]>=mod) c[nowc][i]-=mod;
}
swap(lastc,nowc);
}
for (int i=1;i<=n;i++) {
b[now][i]=0;
for (int j=1;j<=n;j++) b[now][i]=(b[now][i]+a[p][i][j]*b[last][j])%mod;
b[now][i]+=b[last][i];
if (b[now][i]>=mod) b[now][i]-=mod;
}
swap(last,now);
p++;
x>>=1;
}
}
int main() {
scanf ("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++) scanf (" %c",&ch[i][j]),a[0][i][j]=ch[i][j]=='Y'?1ll:0ll;
for (int i=1;i<=n;i++) b[i][i]=1ll;
scanf ("%d%d",&k,&mod);
for (int i=1;i<20 && (1<<i)<=k;i++) {
Multi(a[i-1],a[i-1],a[i]);
for (int j = 1;j <= n;j++,puts(""))
for (int k = 1;k <= n;k++) printf ("%d ",a[i][j][k]);
puts("_-----------");
}
ansn-=(ll)n;
for (int i=1;i<=n;i++) {
Init(i);
fastpow(k-1);
ansn+=c[lastc][i];
ansn%=mod;
}
printf ("%lld\n",ansn);
return 0;
}
推荐阅读