前言
正题
题目链接:https://www.luogu.org/problemnew/show/P3597
题目大意
问第kkk长的路径长度(非简单路径)
解题思路
先考虑kkk比较小时的情况,我们可以求出长度为111的路径,长度为222的路径,然后以此类推找到第一个与前面的和到kkk就可以得出答案。
但是这样并不能通过本题,我们考虑倍增+矩阵乘法倍增+矩阵乘法倍增+矩阵乘法。
首先因为边权只有1,2,31,2,31,2,3所以我们可以拆点,(k−1)∗x(k-1)*x(k−1)∗x表示xxx出发的边上已经走了kkk步。
首先(k∗i)−>(k∗i+k)(k*i)->(k*i+k)(k∗i)−>(k∗i+k),若一条边x−w>yx-_w>yx−w>y那么(w−1)∗x−>y(w-1)*x->y(w−1)∗x−>y就可以了,这样(i,j)(i,j)(i,j)就是iii到jjj的路径条数
用矩阵GkG_kGk中的(i,j)(i,j)(i,j)表示iii到jjj的路径长度≤2k\leq2^k≤2k的有多少条,且(i,0)(i,0)(i,0)表示以iii为终点的长度≤2k\leq2^k≤2k有多少条。
那么有Gk=Gk−1∗Gk−1G_k=G_{k-1}*G_{k-1}Gk=Gk−1∗Gk−1
然后若答案为ansansans,那么有
∑2xi=ans\sum2^{x_{i}}=ans∑2xi=ans且Gover=∏GxiGover=\prod G_{x_i}Gover=∏Gxi的话,那么有
∑i=1n(Gover(i,0)−1)≤k\sum_{i=1}^n (Gover(i,0)-1)\leq ki=1∑n(Gover(i,0)−1)≤k
(减111是因为有一种路径就是一直待在原地,但这不会被计算入答案)
那么我们肯定要求ansansans尽量大,那么我们从大开始枚举一个kkk。
若乘上GkG_kGk后满足条件就可以乘
codecodecode
#include<cstdio>
#include<algorithm>
#include<cstring>
#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);
}