YZOJ P3527 [FJOI2018D1T3]城市路径问题
时间限制:1000MS 内存限制:131072KB
难度:6.5
给出一张 n 个点的有向图 G(V, E) 。对于任意两个点 u, v (u 可以等于 v ),u 向 v 的连边数为:\sum\limits_{i=1}^k {out[u, i] \times in[v, i]} 。
给定 k 和数组 out, in ,现在有 m 个询问,每次询问给出三个参数 u, v, d,你需要回答从节点 u 出发,经过不超过 d 条边到达节点 v 的路径有多少种。
答案对 10^9+7 取模。
第一行两个整数 n, k 。
接下来 n 行,第 i 行有 2k 个整数,前 k 个整数描述 out[i][],后 k 个数描述 in[i][] 。
接下来一行一个整数 m 。
接下来 m 行,每行三个整数 u, v, d,描述一组询问。
对于每个询问,输出一个方案数。由于答案可能太大,输出其除以 10^9+7 后的余数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
5 2 2 5 4 3 7 9 2 4 0 1 5 2 6 3 9 2 2147483647 1000000001 233522 788488 10 1 1 0 2 2 1 2 4 5 4 3 10 3 4 50 1 5 1000 3 5 1000000000 1 2 500000000 4 5 2147483647 3 1 2147483647 |
|
1 51 170107227 271772358 34562176 890241289 8516097 383966304 432287042 326522835 |
对于 40\% 的数据,n \leq 50 ,k \leq 20, m \leq 45 。
对于 90\% 的数据,n \leq 1000, k \leq 20, m \leq 50 。
对于另外 10\% 的数据,n \leq 1000, k=1, m \leq 100 。
所有输入数据不超过
int 范围。
Source: BZOJ 3583 (不是 FJOI 吗?www
首先,若矩阵 A 中 (s, t) 的值表示从 s 到 t 的路径条数,那么 A^d 表示从 s 出发刚好经过 d 条路到达 t 的方案数。
可以考虑矩阵乘法的过程,(i, j) = (i, k) \times (k, j) ,其实就是枚举经过点 k 把方案数乘起来,显然可以得到这个结论。
那么题目说的 “不超过 d 条” 的意思就是 A^0+A^1+A^2+ \cdots + A^d ,暴力算一下就会有 40pts 。
s \to t 的路径条数为 \sum\limits_{i=1}^k {out[s, i] \times in[t, i]} ,也可以看成是一个矩阵乘法的过程,即 OI^T 。
那么要求的是 G^d=\left(OI^T\right)^d ,若使用矩阵快速幂的话时间复杂度为 O(n^2k \log d),因为计算一次 OI^T (O 为 n \times k,I^T 为 k \times n)需要 O(n^2k) 的复杂度,很明显无法通过本题(毕竟是 FJOI 的机子)。
考虑优化,根据矩阵乘法的结合律,有
G^d=\left(OI^T\right)^d = \underbrace{OI^T \times OI^T \times \cdots \times OI^T}_d \\ =O \times \underbrace{I^TO \times I^TO \times \cdots \times I^TO}_{d-1} \times I^T = O\left(I^TO\right)^{d-1}I^T
因为 I^T 为 k \times n,O 为 n \times k,所以计算一次 I^TO 的复杂度只是 O(nk^2),而本题中 k 很小,可以接受。
另外,矩阵幂和没有什么 A^0+A^1+\cdots+A^d = \frac{A^{d+1}-1}{x-1} (不可能有好吧www),要想达到与快速幂同阶的复杂度,需要通过别的方法计算。
注意到
A^0+A^1+A^2+ \cdots A^{2n} = \left(A^0+A^1+\cdots+A^n\right) + A^n \left(A^0+A^1+\cdots+A^n\right)
A^0+A^1+A^2+ \cdots A^{2n+1} = \left(A^0+A^1+\cdots+A^n\right) + A^n \left(A^0+A^1+\cdots+A^n+A^{n+1}\right)
所以可以通过分治法在 O(\log d) 的复杂度内完成。
最后再对于每个询问的 s, t 乘一遍即可,时间复杂度 O\left(nk^2+mk^3\log d\right) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
|
#include <cstdio> #include <cstdlib> #include <cstring> #include <climits> #define MOD 1000000007 inline int getnum() { register char c=0; while(!(c>='0' && c<='9')) c=getchar(); register int a=0; while(c>='0' && c<='9') a=a*10+c-'0',c=getchar(); return a; } int pK; struct _mat { int a[20][20]; _mat(){memset(a,0,sizeof(a));} _mat operator * (const _mat&o)const { _mat ans; for(register int i=0;i<pK;i++) for(register int j=0;j<pK;j++) for(register int k=0;k<pK;k++) (ans.a[i][j]+=(long long)this->a[i][k]*o.a[k][j]%MOD)%=MOD; return ans; } _mat operator + (const _mat&o)const { _mat ans; for(register int i=0;i<pK;i++) for(register int j=0;j<pK;j++) ans.a[i][j]=(this->a[i][j]+o.a[i][j])%MOD; return ans; } }I; int vin[1050][20],vout[1050][20]; _mat base; void PowerSum(_mat&A,_mat&B,register int d) { if(d==1) { A=B=base; return; } PowerSum(A,B,d>>1); B=B+B*A,A=A*A; if(d&1) A=base*A,B=B+A; } int main() { register int N=getnum(),K=getnum(); pK=K; for(register int i=0;i<K;i++) I.a[i][i]=1; for(register int i=0;i<N;i++) { for(register int j=0;j<K;j++) vout[i][j]=getnum()%MOD; for(register int j=0;j<K;j++) vin[i][j]=getnum()%MOD; } for(register int i=0;i<K;i++) for(register int j=0;j<K;j++) for(register int k=0;k<N;k++) (base.a[i][j]+=(long long)vin[k][i]*vout[k][j]%MOD)%=MOD; register int M=getnum(); for(register int lp=1;lp<=M;lp++) { register int u=getnum()-1,v=getnum()-1,d=getnum(); register int ans=(u==v); if(!d) { printf("%d\n",ans); continue; } _mat ansum,ad; if(d>1) PowerSum(ad,ansum,d-1); ansum=ansum+I; for(register int j=0;j<K;j++) for(register int k=0;k<K;k++) (ans+=(long long)vout[u][j]*ansum.a[j][k]%MOD*vin[v][k]%MOD)%=MOD; printf("%d\n",ans); } return 0; } |