[CF1558E]Down Below
Posted StaroForgin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[CF1558E]Down Below相关的知识,希望对你有一定的参考价值。
Down Below
题解
由于题目保证每个点有至少两条边,所以肯定是有解的。
观察到
n
⩽
1
0
3
,
m
⩽
2
×
1
0
3
n\\leqslant 10^3,m\\leqslant 2\\times 10^3
n⩽103,m⩽2×103,所以暴力一点的做法也是没有问题的。
它要求的是开始的权值最小能有多小才能遍历整个图,很容易想到二分这个开始的权值。
可我们如何判断我们二分的权值是否合法呢?跑一遍看它能否遍历整个图就行了。
考虑怎么跑图。
如果没有条件不能走上一次经过的边的条件的话应该很容易想到通过优先队列来维护当前所有可以走到的点,每次选择
a
a
a最小的一个走,但我们不能过去了又回来。
但我们发现我们某个时刻能走到的图一定是一个连通块(指任意两个点之间可以走过去又走回来)加一条从连通块向外延伸的路径的形式。
我们可以考虑只维护我们能走到的连通块,每次扩展时扩展一条路径,使得新得到的图也是个连通块。
我们开始时每个连通块内的点权值就是此刻整个连通块拥有的权值和,我们要找到一条合法的路径可以走到外面又走回联通块内。
我们可以考虑像
b
f
s
bfs
bfs一样向外延伸,有两种情况,一种是我们我们的路径一直延申到我们的联通块内,这种情况明显是可以直接将这条路径加入联通块中的。
而另外一种情况是我们有两条路径到同一个点,由于我们是通过
b
f
s
bfs
bfs查找路径的,所以当我们第一次到达一个有多条路径的点时,一定是某个点可以到一个已经访问到的非前驱的点,这种情况我们到这个点的两条路经过的边是不同的,而我们的权值又是不降的,所以我们肯定可以将两条路径连接再一起得到一条新路径。
所以我们只需要再可以到达一个已
b
f
s
bfs
bfs过的非前驱的点或联通快内的点,将来的两条路径连再一起即可。
时间复杂度
O
(
n
m
l
o
g
n
)
O\\left(nmlog\\,n\\right)
O(nmlogn)
~虽然这做法看着很暴力,但确实能过。
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2005
#define MAXM 2005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int inv2=499122177;
const int jzm=2333;
const int zero=10000;
const int lim=30000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int T,n,m,head[MAXN],tot,a[MAXN],b[MAXN],pre[MAXN];LL dis[MAXN];
bool fg[MAXN],vis[MAXN];
queue<int>q;
struct edge{int to,nxt;}e[MAXN<<1];
void addEdge(int u,int v){e[++tot]=(edge){v,head[u]};head[u]=tot;}
bool check(int x){
for(int i=1;i<=n;i++)fg[i]=0;fg[1]=1;int cnt=1;
while(cnt<n){
while(!q.empty())q.pop();LL summ=x,X=0,Y=0;
for(int i=1;i<=n;i++){if(fg[i])summ+=b[i];vis[i]=dis[i]=0;}
for(int i=1;i<=n;i++)if(fg[i])q.push(i),dis[i]=summ,vis[i]=1;
while(!q.empty()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;if(fg[u]&&fg[v])continue;
if(dis[u]<=a[v]||pre[u]==v)continue;
if(vis[v]){X=u;Y=v;break;}
dis[v]=dis[u]+1ll*b[v];vis[v]=1;pre[v]=u;q.push(v);
}
if(X+Y)break;
}
if(!(X+Y))return 0;
while(!fg[X])fg[X]=1,X=pre[X],cnt++;
while(!fg[Y])fg[Y]=1,Y=pre[Y],cnt++;
//for(int i=1;i<=n;i++)printf("%d ",fg[i]);puts("");
}
return 1;
}
signed main(){
read(T);
while(T--){
read(n);read(m);
for(int i=2;i<=n;i++)read(a[i]);
for(int i=2;i<=n;i++)read(b[i]);
for(int i=1,u,v;i<=m;i++)read(u),read(v),addEdge(u,v),addEdge(v,u);
int l=1,r=1e9+1;while(l<r){int mid=l+r>>1;if(check(mid))r=mid;else l=mid+1;}
printf("%d\\n",l);
for(int i=1;i<=n;i++)head[i]=0;tot=0;
}
return 0;
}
谢谢!!!
以上是关于[CF1558E]Down Below的主要内容,如果未能解决你的问题,请参考以下文章