📅  最后修改于: 2023-12-03 15:42:22.434000             🧑  作者: Mango
本题是门
系列题目的第43题,题目难度为普及+/提高,需要掌握较为扎实的基础知识和算法能力。题目描述如下:
给定一张 $n$ 个点,$m$ 条边的有向图,每个点有一个初始的点权 $w_i$,现在需要进行 $q$ 次操作,每次操作为以下两种之一:
请你编写一个程序,实现以上功能。
本题是一个典型的图论问题,需要运用复杂的算法解决。通常我们会采用以下两种算法来解决此类问题:
下面简单介绍一下这两种算法的原理和操作流程:
假设 $g(x)$ 表示从点 $x$ 出发,经过若干条边后到达的节点中点权最小值的最大值。如果 $g(x)$ 比 $g(y)$ 大,那么从点 $x$ 到点 $y$ 的所有路径中,点权最小值的最大值显然为 $g(x)$。
考虑如何求解 $g(x)$。我们可以先将原图反向,然后预处理出从每个点 $x$ 出发,经过若干条边后的最小值 $h(x)$,以及从每个点 $x$ 出发,经过若干条边前的最小值 $f(x)$。这可以通过动态规划实现,具体见代码。
对于一个点 $x$,其 $g(x)$ 的取值显然在 $[f(x), w_x]$ 中,因为如果在从 $x$ 开始的路径上选择了一个小于 $f(x)$ 的点,那么最大能选择的点权必定小于 $f(x)$;而如果选择了大于 $w_x$ 的点,那么最小值必定大于 $w_x$,也是不符合要求的。
因此,我们可以从小到大枚举 $g(x)$ 的取值,然后判断从起点到终点是否有一条路径,使得路径上所有点的点权都不小于当前 $g(x)$ 的取值。具体见代码。
时间复杂度:$O((n+m)qlogw)$,其中 $w$ 为点权的取值范围。
假设 $g(x)$ 表示从点 $x$ 出发,经过若干条边后到达的节点中点权最小值的最大值。同样地,如果 $g(x)$ 比 $g(y)$ 大,那么从点 $x$ 到点 $y$ 的所有路径中,点权最小值的最大值显然为 $g(x)$。
考虑如何二分答案求解 $g(x)$。我们可以建立一个新图,其中每条边 $(u,v)$ 的长度为 $w_v$,然后在新图上从 $x$ 出发,求出到达所有点的最长路,然后将得到的最长路长度与当前的二分答案比较,如果大于等于,则说明解在当前的二分范围内,否则说明解在之前的范围里。
时间复杂度:$O((n+m)qlogw)$,其中 $w$ 为点权的取值范围。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=10005;
int n,m,q,cnt;
int head[MAXN],hhead[MAXN];
int w[MAXN],f[MAXN],h[MAXN];
bool vis[MAXN];
struct edge{
int to,val,nxt;
}e[MAXN<<1],he[MAXN<<1];
void add(int u,int v,int val){
e[++cnt].to=v;
e[cnt].val=val;
e[cnt].nxt=head[u];
head[u]=cnt;
}
void hadd(int u,int v,int val){
he[++cnt].to=v;
he[cnt].val=val;
he[cnt].nxt=hhead[u];
hhead[u]=cnt;
}
void dfs1(int u,int fa,int minn){
f[u]=minn;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v!=fa) dfs1(v,u,min(minn,w[v]));
}
}
void dfs2(int u,int fa,int maxx){
h[u]=maxx;
for(int i=hhead[u];i;i=he[i].nxt){
int v=he[i].to;
if(v!=fa) dfs2(v,u,max(maxx,w[v]));
}
}
bool check(int u,int v,int val){
vis[u]=1;
if(u==v) return 1;
for(int i=head[u];i;i=e[i].nxt){
int w=e[i].to;
if(vis[w]||f[w]>val) continue;
if(check(w,v,val)) return 1;
}
for(int i=hhead[u];i;i=he[i].nxt){
int w=he[i].to;
if(vis[w]||h[w]<val) continue;
if(check(w,v,val)) return 1;
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
while(m--){
int u,v,val;
scanf("%d%d%d",&u,&v,&val);
add(u,v,val);
hadd(v,u,val);
}
dfs1(1,0,w[1]);
dfs2(1,0,w[1]);
scanf("%d",&q);
while(q--){
int op,x,y;
scanf("%d%d",&op,&x);
if(op==1){
scanf("%d",&y);
w[x]=y;
memset(vis,0,sizeof(vis));
}
else{
scanf("%d",&y);
int l=f[x],r=w[x],ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(x,y,mid)){
ans=mid; l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
}
}
return 0;
}
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=10005;
int n,m,q,cnt,dis[MAXN];
int head[MAXN],w[MAXN];
bool vis[MAXN];
struct edge{
int to,val,nxt;
}e[MAXN<<1];
void add(int u,int v,int val){
e[++cnt].to=v;
e[cnt].val=val;
e[cnt].nxt=head[u];
head[u]=cnt;
}
bool check(int s,int t,int val){
memset(dis,-INF,sizeof(dis));
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(s); dis[s]=0;
while(!q.empty()){
int u=q.front(); q.pop(); vis[u]=0;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(dis[v]<dis[u]+(w[v]>=val)*e[i].val){
dis[v]=dis[u]+(w[v]>=val)*e[i].val;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return dis[t]>=0;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
while(m--){
int u,v,val;
scanf("%d%d%d",&u,&v,&val);
add(u,v,val);
}
scanf("%d",&q);
while(q--){
int op,x,y;
scanf("%d%d",&op,&x);
if(op==1){
scanf("%d",&y);
w[x]=y;
}
else{
scanf("%d",&y);
int l=1,r=1e9,ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(x,y,mid)){
ans=mid; l=mid+1;
}
else r=mid-1;
}
printf("%d\n",ans);
}
}
return 0;
}
参考文献:https://www.luogu.com.cn/blog/293225/yi-zhang-zong-jie-men-men-cs-2010-di-sec43ti