最短路

直接应用

P3953 [NOIP2017 提高组] 逛公园

这道题居然想到了状态:\(f[i][j]\) 表示节点 \(i\) 经过距离比最短路多 \(j\) 的方案数,由于 \(k\) 特别小,所以第二维可以装下。转移也比较容易

关于这题零环的判断以及多次绕正环的处理比较麻烦,于是采用新科技——倒叙dfs遍历
因为从终点开始遍历可以减去很多超过 \(k\) 的枝,对于零环,每次遍历都用 \(bool\) 打标记,如果一个点在相同深度经过了两次,说明有零环直接退就可以了

代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5,maxm=4e5+5;
int n,m,dis[maxn],t,ans,mod,hd[maxn],hd1[maxn],cnt1,cnt,f[maxn][55],x,y,w,k;
bool vis1[maxn][55],vis[maxn],flag;
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();	
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}	
struct Edge{
	int nxt,to,val;
}edge[maxm],edge1[maxm];
void add(int u,int v,int w){
	edge[++cnt].nxt=hd[u];
	edge[cnt].to=v;
	edge[cnt].val=w;
	hd[u]=cnt;
	return ;	
}
void add1(int u,int v,int w){
	edge1[++cnt1].nxt=hd1[u];
	edge1[cnt1].to=v;
	edge1[cnt1].val=w;
	hd1[u]=cnt;
	return ;	
}
struct Node{
	int dis,id;
	Node(){}
	Node(int x,int y):dis(x),id(y){}
	bool operator < (const Node & x)const{
		return dis>x.dis;
	}
};
priority_queue<Node>q;
void dij(){
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[1]=0;
	q.push(Node(0,1));
	while(!q.empty()){
		int u=q.top().id;
		q.pop();
		if(vis[u])continue;
		vis[u]=true;
		for(int i=hd[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].val){
				dis[v]=dis[u]+edge[i].val;
				q.push(Node(dis[v],v));
			}	
		}
	}
	return ;
}
int dfs(int u,int deep){
	if(deep<0||deep>k)return 0;
	if(vis1[u][deep]){
		vis1[u][deep]=false;
		return -1;
	}
	if(~f[u][deep])return f[u][deep];
	int sum=0;
	vis1[u][deep]=true;
	for(int i=hd1[u];i;i=edge1[i].nxt){
		int v=edge1[i].to;
		w=dfs(v,dis[u]+deep-dis[v]-edge1[i].val);
		if(w==-1){
			vis1[u][deep]=false;
			return -1;
		}
		sum=(sum+w)%mod;
	}
	vis1[u][deep]=false;
	if(u==1&&deep==0)sum=1;
	f[u][deep]=sum;
	return sum;
}
int main(){
	t=read();
	while(t--){
		n=read();
		m=read();
		k=read();
		mod=read();
		ans=0;
		cnt=cnt1=0;
		memset(hd,0,sizeof hd);
		memset(hd1,0,sizeof hd1);
		for(int i=1;i<=m;i++){
			x=read();
			y=read();
			w=read();
			add(x,y,w);
			add1(y,x,w);
		}
		dij();
//		cout<<dis[n]<<endl;
		memset(f,-1,sizeof f);
		flag=false;
		for(int i=0;i<=k;i++){
			w=dfs(n,i);
			if(w==-1){
				flag=true;
				break;	
			}
			ans=(ans+w)%mod;
		}
		if(flag)puts("-1");
		else cout<<ans<<endl;
	}
	return 0;
}

CF1163F Indecisive Taxi Fee

首先看清题——修改是暂时的,只对当前询问有效!!!

可以分类讨论:

  1. 不在最短路上,变大了:答案不变
  2. 不在最短路上,变小了:答案可能变成 \(dis(1,u)+dis(v,n)+val\)
  3. 在最短路上,变小了:答案可能变成 \(dis(1,n)-edge[i].val+val\)
  4. 在最短路上,变大了:这时是最棘手的情况,可能最短路变成了其他路径,然而并不一定是原图上的次短路,因为次短路可能也用到了这段路径

于是引入新科技——线段树来求解
首先预处理出原图的一条最短路径,并记录上面所有的边、点
线段树维护不经过路径上区间 \([l,r]\) 的点的最短路长度
至于维护,需要预处理另外一个东西:

pic.PNG

像这张图一样,\(l[u]\) 表示从1到 \(u\) 这个点经过最短路上最后一条边是哪一个,\(r[v]\) 同理
这样对于每条边,更新其端点限制的一段区间即可,即 \(l[u]+1,r[v]-1\)

代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e5+5;
const int maxm=4e5+5;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,m,qq,hd[maxn],cnt,x,y,w,ans,dis1[maxn],dis[maxn],id[maxn],pre[maxn],l[maxn],r[maxn],tot;
bool vis[maxn],one[maxn],onp[maxn];
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();	
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
struct Edge{
	int nxt,to,from,val;	
}edge[maxm];
void add(int u,int v,int w){
	edge[++cnt].nxt=hd[u];
	edge[cnt].to=v;
	edge[cnt].val=w;
	edge[cnt].from=u;
	hd[u]=cnt;
	return ;	
}
struct Node{
	int dis,id;
	Node(){}
	Node(int x,int y):dis(x),id(y){}
	bool operator < (const Node & x)const{
		return dis>x.dis;
	}
};
priority_queue<Node>q;
void dij(int s,int op){
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[s]=0;
	q.push(Node(dis[s],s));
	while(!q.empty()){
		int u=q.top().id;
		q.pop();
		if(vis[u])continue;
		vis[u]=true;
		for(int i=hd[u];i;i=edge[i].nxt){
			int v=edge[i].to;
			if(dis[v]>dis[u]+edge[i].val){
				if(!op)pre[v]=i;
				if(op==1&&(!onp[v]))l[v]=l[u];
				if(op==2&&(!onp[v]))r[v]=r[u];
				dis[v]=dis[u]+edge[i].val;
				q.push(Node(dis[v],v));	
			}
		}
	}
	return ;
}

struct Seg{
	int l,r,val;	
}t[maxn*4];
void build(int p,int l,int r){
	t[p].l=l;
	t[p].r=r;
	t[p].val=inf;
	if(l==r){
//		t[p].val=inf;
		return ;
	}
	int mid=l+r>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	return ;
}
void spread(int p){
	t[p<<1].val=min(t[p<<1].val,t[p].val);
	t[p<<1|1].val=min(t[p<<1|1].val,t[p].val);
	return ;	
}
void change(int p,int l,int r,int w){
	if(l>r)return ;
//	cout<<t[p].l<<" "<<t[p].r<<" "<<l<<" "<<r<<endl;
	if(t[p].l>=l&&t[p].r<=r){t[p].val=min(t[p].val,w);return ;}
	spread(p);
	int mid=t[p].l+t[p].r>>1;
	if(l<=mid)change(p<<1,l,r,w);
	if(r>mid)change(p<<1|1,l,r,w);
	return ;	
}
void find_path(){
	for(int u=1;u;u=edge[pre[u]^1].to){
		onp[u]=true;
		one[pre[u]>>1]=true;
		id[pre[u]>>1]=++tot;
		l[u]=tot-1;
		r[u]=tot;
	}
	tot--;
	return ;
}
int ask(int p,int pos){
	if(t[p].l==t[p].r&&t[p].l==pos)return t[p].val;
	spread(p);
	int mid=t[p].l+t[p].r>>1;
	if(pos<=mid)return ask(p<<1,pos);
	else return ask(p<<1|1,pos);	
}
signed main(){
	cnt=1;
	n=read();
	m=read();
	qq=read();
	for(int i=1;i<=m;i++){
		x=read();
		y=read();
		w=read();
		add(x,y,w);
		add(y,x,w);
	}
	dij(n,0);
	find_path();
//	cout<<tot<<endl;
//	cout<<"hhh";
	dij(1,1);
	memcpy(dis1,dis,sizeof dis1);
	dij(n,2);
	build(1,1,tot);
//	cout<<"hhh";
	for(int i=2;i<=cnt;i++){
		if(!one[i>>1]){
			int u=edge[i].from;
			int v=edge[i].to;
			change(1,l[u]+1,r[v]-1,dis1[u]+dis[v]+edge[i].val);	
		}
	}
//	for(int i=1;i<=n;i++)cout<<dis[i]<<" ";
//	cout<<endl;
//	cout<<dis1[n]<<endl;
	for(int i=1;i<=qq;i++){
		x=read();
		w=read();
		int u=edge[x<<1].from;
		int v=edge[x<<1].to;
//		cout<<"hhh "<<u<<" "<<v<<" "<<one[x]<<endl;
		ans=dis1[n];
		if(!one[x]){
			ans=min(ans,dis1[u]+dis[v]+w);
			ans=min(ans,dis1[v]+dis[u]+w);
		}
		else{
			ans=ans-edge[x<<1].val+w;
			if(w>edge[x<<1].val){
				ans=min(ans,ask(1,id[x]));	
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-06-17 21:59  y_cx  阅读(67)  评论(0编辑  收藏  举报