CF576D Flights for Regular Customers

题意

给出一张有向图,对于边\(i\)有限制条件\(d_i\),表示在走边\(i\)前必须走过至少\(d_i\)条其他的边。为从\(1\)\(n\)最少要走几条边。
无解输出\(Impossible\)
\(n \leq 150,m \leq 150,d_i \leq 10^9\)

思路

特别棒的一篇
首先按照\(d\)的大小升序排序
然后分成\(m\)个时刻处理,\(can[i][j]\)表示当前时刻\(i\)能否走到\(j\)

更新\(can\)即通过上一次时刻乘上邻接矩阵的\(d_i-d_{last}\)次方(注意邻接矩阵不断变化,每次处理完再加上一条边)
更新答案的时候,我们枚举每个点。
\(1\)可以走到\(i\)点,则更新答案为\(d_i(当前时刻走到i)+dis[i][n](从i还要走的路)\)
最短路由\(floyd\)求解即可。考虑到d很大,用矩阵快速幂加速。
矩阵乘法的时候用\(bitset\)优化。


#include <bits/stdc++.h>
using namespace std;
const int N=160;
int ans=0x3f3f3f3f,n,m,dis[N][N],last;
bitset<N> can[N],a[N],t[N];
struct edge{
	int x,y,d;
}e[N];
bool cmp(edge x,edge y){
	return x.d<y.d;
}
void mul(bitset<N> *a,bitset<N> *b){
	bitset<N> tt[N];
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (a[i][j]) tt[i]|=b[j];
	for (int i=1;i<=n;i++) a[i]=tt[i];
}
void ksm(bitset<N> *a,int y){
	bitset <N> Ans[N];
	for (int i=1;i<=n;i++) Ans[i][i]=1;
	for (;y;y>>=1,mul(a,a))
		if (y&1) mul(Ans,a);
	for (int i=1;i<=n;i++) a[i]=Ans[i];
}
int main(){
	scanf("%d%d",&n,&m);
	for (int i=1;i<=m;i++)
		scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].d);
	sort(e+1,e+m+1,cmp);
	memset(dis,0x3f,sizeof(dis));
	for (int i=1;i<=n;i++) dis[i][i]=0,can[i][i]=1;
	for (int i=1;i<=m;i++){
		int x=e[i].x,y=e[i].y,d=e[i].d;
		for (int j=1;j<=n;j++) t[j]=a[j];
		ksm(t,d-last);
		mul(can,t);
		for (int j=1;j<=n;j++)
			for (int k=1;k<=n;k++)
				dis[j][k]=min(dis[j][k],dis[j][x]+1+dis[y][k]);
		for (int j=1;j<n;j++) if (can[1][j]) {
			ans=min(ans,d+dis[j][n]);
		}
		last=d;
		a[x][y]=1;
	}
	if (ans<0x3f3f3f3f) printf("%d\n",ans);
	else puts("Impossible");
	return 0;
}

后记

就是不停的挂,比如什么\(i,j\)或者\(n,N\)写反
还有\(ans\)的初始化不能太大也不能太小

posted @ 2019-10-22 10:49  flyfeather  阅读(198)  评论(0编辑  收藏  举报