P2685 题解

P2685

简化版:P1186

思路

题意即:对于一个图任意断一条边后形成的最短路最长的长度和方案数。

显然,断在原图最短路上最优。新最短路一定是在原最短路的 \(u\) 点离开最短路,再在 \(v\) 点回到原最短路。

\(lst_u\) 表示原最短路和从 \(1\)\(u\) 的最短路的最后一个交点。设 \(fir_u\) 表示原最短路和从 \(u\)\(n\) 的最短路的第一个交点。

\(ans_i\) 表示第 \(i\) 条原最短路边被断后新最短路的长度。最后答案 \(res\) 等于 \(\max^{tp-1}_{i=1} ans_i\)

对于一条从 \(u\)\(v\) 的边 \(i\),如果这条边是新路的一部分,这条新路长度 \(val_i\) 等于 \(dis1_u+w_i+disn_v\),断边在 \(lst_u\)\(fir_v\) 之间。对于原最短路边 \(j\) 满足 \(lst_u\leq j< fir_v\)\(ans_j\) 可以被 \(val_i\) 更新,\(ans_j\) 取最小值。

区间更新,维护最小值,联想到线段树。对 \(tp-1\) 条边建树。最后 \(ans_i\) 的值即 \(l=r=i\) 时叶子节点的值。因此这个线段树只需要处理下传,不需要上传。

code

确定原来的最短路。用递推的方式,如果 \(disn_v+w(u,v)=disn_u\),说明 \(v\)\(u\)\(n\) 。最短路上复杂度 \(O(n+m)\)

对于每个最短路上的点,沿着非原最短路、没有处理过的点并保证 \(u\)\(1\)\(v\) 的最短路上搜索,遇到的点更新 \(lst_v\)\(fir_v\) 则反过来。每个点一共只经过一次。复杂度 \(O(n)\)

int tree[maxn<<2],ans[maxn];
void build(int nd,int l,int r){
	tree[nd]=inf;
	if(l==r)return ;
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	build(ls,l,mid);build(rs,mid+1,r);
}
void updata(int nd,int l,int r,int ql,int qr,int w){
	if(l>=ql&&r<=qr){
		tree[nd]=min(tree[nd],w);
		return ;
	}
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	if(ql<=mid)updata(ls,l,mid,ql,qr,w);
	if(qr>mid)updata(rs,mid+1,r,ql,qr,w);
}
void down(int nd,int l,int r){
	if(l==r){
		ans[l]=tree[nd];
		return;
	}
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	tree[ls]=min(tree[nd],tree[ls]);
	tree[rs]=min(tree[nd],tree[rs]);
	down(ls,l,mid);down(rs,mid+1,r);
}

枚举每条非原最短路边并更新。如果最后答案还是原最短路长度,那断任何一条边都可以。

int n,m;
int head[maxn],tot;
struct nd{
	int nxt,to,w;
}e[maxn<<2];
void add(int u,int v,int w){
	e[++tot].nxt=head[u];e[tot].to=v;e[tot].w=w;
	head[u]=tot;
}
int u,v,w;

struct Dis{
	int id,dis;
	bool operator <(const Dis&tmp)const{return dis>tmp.dis;};
};
int dis1[maxn],disn[maxn];
bool bk[maxn];
priority_queue<Dis> q;
void dij1(){
	memset(dis1,0x3f,sizeof(dis1));
	dis1[1]=0;q.push({1,dis1[1]});
	while(!q.empty()){
		u=q.top().id;q.pop();
		if(bk[u])continue;
		bk[u]=true;
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(dis1[v]>dis1[u]+e[i].w){
				dis1[v]=dis1[u]+e[i].w;
				q.push({v,dis1[v]});
			}
		}
	}
}
void dijn(){
	memset(disn,0x3f,sizeof(disn));
	memset(bk,false,sizeof(bk));
	disn[n]=0;q.push({n,disn[n]});
	while(!q.empty()){
		u=q.top().id;q.pop();
		if(bk[u])continue;
		bk[u]=true;
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(disn[v]>disn[u]+e[i].w){
				disn[v]=disn[u]+e[i].w;
				q.push({v,disn[v]});
			}
		}
	}
}

int d[maxn],tp,id[maxn];
int lst[maxn],fir[maxn];
bool vis[maxn<<2];
queue<int> q1;
void bfs1(int x){
	q1.push(d[x]);lst[d[x]]=x;
	while(!q1.empty()){
		u=q1.front();q1.pop();
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(!id[v]&&!lst[v]&&dis1[u]+e[i].w==dis1[v]){
				lst[v]=x;
				q1.push(v);
			}
		}
	}
}
void bfsn(int x){
	q1.push(d[x]);fir[d[x]]=x;
	while(!q1.empty()){
		u=q1.front();q1.pop();
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(!id[v]&&!fir[v]&&disn[u]+e[i].w==disn[v]){
				fir[v]=x;
				q1.push(v);
			}
		}
	}
}

int tree[maxn<<2],ans[maxn];
void build(int nd,int l,int r){
	tree[nd]=inf;
	if(l==r)return ;
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	build(ls,l,mid);build(rs,mid+1,r);
}
void updata(int nd,int l,int r,int ql,int qr,int w){
	if(l>=ql&&r<=qr){
		tree[nd]=min(tree[nd],w);
		return ;
	}
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	if(ql<=mid)updata(ls,l,mid,ql,qr,w);
	if(qr>mid)updata(rs,mid+1,r,ql,qr,w);
}
void down(int nd,int l,int r){
	if(l==r){
		ans[l]=tree[nd];
		return;
	}
	int mid=l+r>>1,ls=nd<<1,rs=nd<<1|1;
	tree[ls]=min(tree[nd],tree[ls]);
	tree[rs]=min(tree[nd],tree[rs]);
	down(ls,l,mid);down(rs,mid+1,r);
}
int mn,cnt;

signed main(){
	n=read();m=read();
	if(n==1){
		printf("0 1\n");
		return 0;
	}
	for(int i=1;i<=m;i++){
		u=read();v=read();w=read();
		if(u==v)continue;
		add(u,v,w);add(v,u,w);
	}
	dij1();dijn();u=1;
	while(u<n){
		d[++tp]=u;id[u]=tp;
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(disn[v]+e[i].w==disn[u]){
				vis[i]=1;u=v;
				break;
			}
		}
	}
	d[++tp]=u;id[u]=tp;
	for(int i=1;i<=tp;i++)bfs1(i);
	for(int i=tp;i>=1;i--)bfsn(i);
	tp--;build(1,1,tp);
	for(u=1;u<=n;u++){
		for(int i=head[u];i;i=e[i].nxt){
			v=e[i].to;
			if(!vis[i]&&lst[u]<fir[v]){
				updata(1,1,tp,lst[u],fir[v]-1,dis1[u]+e[i].w+disn[v]);
			}
		}
	}
	down(1,1,tp);
	for(int i=1;i<=tp;i++){
		if(mn<ans[i])mn=ans[i],cnt=1;
		else if(mn==ans[i])++cnt;
	}
	if(mn==dis1[n])cnt=m;
	printf("%lld %lld\n",mn,cnt);
}

upd

被 hack 了。要考虑自环。可以发现不会选择断自环,也不会选择走自环,特判即可。代码已改。

posted @ 2024-05-10 20:02  yhddd  阅读(26)  评论(0)    收藏  举报