矩阵优化图上问题学习笔记
参考资料:xzy's blog,orz xiaoziyao!
对于有边权且边权小的图,考虑拆点 \((i,j)\) 表示还有 \(j\) 步走到 \(i\),则显然有连边 \((a,0)\to (b,c-1)\) 和 \((i,j)\to (i,j-1)\)
CF576D Flights for Regular Customers
考虑时间顺序加边,每次加边后判断是否能到点 \(i\),如果能到的话则用时间加上当前图的这个点到终点的最短路更新答案。
维护能否到达其实只用上一次的矩阵乘以此时的邻接矩阵的 \(d_i-d_{i-1}\) 次幂即可,由于都是 \(01\) 矩阵,可以用 bitset 优化。
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
const int mod=1e9+7;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=151;
struct edge{int a,b,d;}e[N];
inline int cmp(edge x,edge y){return x.d<y.d;}
struct mat{bitset<N>a[N];}now,g;
int n,m,dis[N][N],ans=inf;
inline mat mul(mat x,mat y){
mat tmp;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(x.a[i][j])tmp.a[i]|=y.a[j];
return tmp;
}
inline void ksm(mat x,int y,mat &res){
while(y){
if(y&1)res=mul(res,x);
x=mul(x,x);y>>=1;
}
}
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++)
e[i].a=read(),e[i].b=read(),e[i].d=read();
sort(e+1,e+1+m,cmp);
for(int i=1;i<=n;i++)now.a[i][i]=1;
for(int i=1;i<=m;i++){
ksm(g,e[i].d-e[i-1].d,now);
g.a[e[i].a][e[i].b]=1;
for(int a=1;a<=n;a++)
for(int b=1;b<=n;b++)
dis[a][b]=(g.a[a][b]?1:(a==b?0:inf));
for(int k=1;k<=n;k++)
for(int a=1;a<=n;a++)
for(int b=1;b<=n;b++)
dis[a][b]=min(dis[a][b],dis[a][k]+dis[k][b]);
for(int a=1;a<=n;a++)
if(now.a[1][a])ans=min(ans,e[i].d+dis[a][n]);
}if(ans==inf)return puts("Impossible")&0;
printf("%d\n",ans);
return 0;
}
NOI2020 美食家
重定义矩阵乘法:\(A\times B=C\to C_{i,j}=\max_k A_{i,k}+B_{k,j}\)
这样复杂度就是 \(O((5n)^3 \log T k)\),发现我们只需要 \(A_{1,1}\),于是只维护一行,复杂度降为 \(O((5n)^3 \log T+(5n)^2 k \log T)\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define inf 4e18
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
const int N=300;
int n,m,T,k,tot,c[N],id[N][6],A[N],tp[N];
struct mat{
int a[N][N];
inline void init(){
for(int i=1;i<N;i++)
for(int j=1;j<N;j++)a[i][j]=-inf;
}
}pw[50];
inline void mul(mat &B,mat A){
for(int i=1;i<=tot;i++)
for(int j=1;j<=tot;j++){
B.a[i][j]=-inf;
for(int k=1;k<=tot;k++)
B.a[i][j]=max(B.a[i][j],A.a[i][k]+A.a[k][j]);
}
}
struct fes{int t,x,y;}F[maxn];
inline int cmp(fes x,fes y){return x.t<y.t;}
inline void Mul(mat B){
for(int i=1;i<=tot;i++){
tp[i]=-inf;
for(int j=1;j<=tot;j++)
tp[i]=max(tp[i],A[j]+B.a[j][i]);
}
for(int i=1;i<=tot;i++)A[i]=tp[i];
}
signed main(){
n=read(),m=read(),T=read(),k=read();pw[0].init();
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++)id[i][0]=++tot;
for(int i=1,u,v,w;i<=m;i++){
u=read(),v=read(),w=read();
for(int i=1;i<w;i++){
if(!id[v][i])id[v][i]=++tot;
pw[0].a[id[v][i]][id[v][i-1]]=0;
}pw[0].a[u][id[v][w-1]]=c[v];
}
for(int i=1;i<=30;i++)mul(pw[i],pw[i-1]);
for(int i=1;i<=tot;i++)A[i]=-inf;A[1]=c[1];
for(int i=1;i<=k;i++)F[i].t=read(),F[i].x=read(),F[i].y=read();
sort(F+1,F+1+k,cmp);F[k+1]=(fes){T,0,0};
for(int i=1;i<=k+1;i++){
int dt=F[i].t-F[i-1].t;
for(int j=30;~j;--j)if(dt>>j&1)Mul(pw[j]);
A[F[i].x]+=F[i].y;
}if(A[1]<0)puts("-1");
else printf("%lld\n",A[1]);
return 0;
}