题目链接:https://www.luogu.org/problemnew/show/P3597
问第 k k k长的路径长度(非简单路径)
先考虑 k k k比较小时的情况,我们可以求出长度为 1 1 1的路径,长度为 2 2 2的路径,然后以此类推找到第一个与前面的和到 k k k就可以得出答案。
但是这样并不能通过本题,我们考虑 倍 增 + 矩 阵 乘 法 倍增+矩阵乘法 倍增+矩阵乘法。
首先因为边权只有 1 , 2 , 3 1,2,3 1,2,3所以我们可以拆点, ( k − 1 ) ∗ x (k-1)*x (k−1)∗x表示 x x x出发的边上已经走了 k k k步。
首先 ( k ∗ i ) − > ( k ∗ i + k ) (k*i)->(k*i+k) (k∗i)−>(k∗i+k),若一条边 x − w > y x-_w>y x−w>y那么 ( w − 1 ) ∗ x − > y (w-1)*x->y (w−1)∗x−>y就可以了,这样 ( i , j ) (i,j) (i,j)就是 i i i到 j j j的路径条数
用矩阵 G k G_k Gk中的 ( i , j ) (i,j) (i,j)表示 i i i到 j j j的路径长度 ≤ 2 k \leq2^k ≤2k的有多少条,且 ( i , 0 ) (i,0) (i,0)表示以 i i i为终点的长度 ≤ 2 k \leq2^k ≤2k有多少条。
那么有 G k = G k − 1 ∗ G k − 1 G_k=G_{k-1}*G_{k-1} Gk=Gk−1∗Gk−1
然后若答案为 a n s ans ans,那么有
∑ 2 x i = a n s \sum2^{x_{i}}=ans ∑2xi=ans且 G o v e r = ∏ G x i Gover=\prod G_{x_i} Gover=∏Gxi的话,那么有
∑ i = 1 n ( G o v e r ( i , 0 ) − 1 ) ≤ k \sum_{i=1}^n (Gover(i,0)-1)\leq k i=1∑n(Gover(i,0)−1)≤k
(减 1 1 1是因为有一种路径就是一直待在原地,但这不会被计算入答案)
那么我们肯定要求 a n s ans ans尽量大,那么我们从大开始枚举一个 k k k。
若乘上 G k G_k Gk后满足条件就可以乘
#include
#include
#include
#define ll long long
using namespace std;
const ll Size=125;
struct matrix{
ll a[Size][Size];
}G[70],ove;
ll n,m,ans,k;
matrix operator *(matrix &a, matrix &b) {
matrix c;memset(c.a,0,sizeof(c.a));
for(ll i=0;i<Size;i++)
for(ll j=0;j<Size;j++)
for(ll k=0;k<Size;k++)
c.a[i][j]+=a.a[i][k]*b.a[k][j];
return c;
}
bool check(matrix a){
ll sum=0;
for(ll i=1;i<=n;i++)
if(k<=(sum+=a.a[i][0]-1)) return 1;
return 0;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&k);
G[0].a[0][0]=1;
for(ll i=1;i<=n;i++)
ove.a[i][i]=G[0].a[i][i+n]=G[0].a[i+n][i+2*n]=G[0].a[i][0]=1;
for(ll i=1;i<=m;i++){
ll x,y,w;
scanf("%lld%lld%lld",&x,&y,&w);
G[0].a[x+(w-1)*n][y]++;
}
ll tot=0;matrix tmp;
while(++tot){
if(tot>65)
return puts("-1")&0;
G[tot]=G[tot-1]*G[tot-1];
if(check(G[tot])) break;
}
while((--tot)>=0){
tmp=ove*G[tot];
if(!check(tmp))
ans+=(1ll<<tot),ove=tmp;
}
printf("%lld",ans);
}