@ysner
2018-06-19T22:42:56.000000Z
字数 3263
阅读 2058
图论
强联通分量
给定一个有个顶点条边的带权无向图。现有次询问,每次询问之间的一条环路径(无重边),要求边权最大值最小,输出这个最大值。
暴搜。记得和的过程要连起来,后者从前者引出。
而不是
dfs(u,v,lim);
if(!flag) return;
dfs(v,u,lim);
。。。
边双无割边(转一圈走两次的边)。
点双无割点(转一圈走两次的点)。(一个环)
所以这道题应用边双,与点双唯一的不同是没有判定过程。
il int tarjan(re int u,re ll lim,re int lst)
{
dfn[u]=low[u]=++tim;sta[++top]=u;re int v;
for(re int i=h[u];i+1;i=e[i].nxt)
{
v=e[i].to;
if(e[i].w>lim||lst==i) continue;
if(!dfn[v]) tarjan(v,lim,i^1),low[u]=min(low[u],low[v]);
else low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
do
{
v=sta[top--];
f[v]=u;
}
while(u^v);
}
最大值、最小值,容易想到二分。
于是二分这个值,每次排除掉边权比更大的边,找边强联通分量。
什么,你说这是无向图?
时不走上次走的边的反边就可以啦。
复杂度
il int check(re int u,re int tar,re ll lim)
{
top=tim=0;fp(i,1,n) dfn[i]=low[i]=0,f[i]=i;
fp(i,1,n) if(!dfn[i]) tarjan(i,lim,-1);
return find(u)==find(tar);
}
int main()
{
n=gi();m=gi();q=gi();
memset(h,-1,sizeof(h));
fp(i,1,n) num[i]=gi();
fp(i,1,m)
{
re int u=gi(),v=gi();ll w=num[u]-num[v];if(w<0) w=-w;
add(u,v,w);add(v,u,w);if(mx<w) mx=w;if(mn>w) mn=w;
}
while(q--)
{
re int u=gi(),v=gi();
re ll l=mn,r=mx,ans=-1;
while(l<=r)
{
re ll mid=l+r>>1;
if(check(u,v,mid))r=mid-1,ans=mid;
else l=mid+1;
}
ans==-1?puts("No way!"):printf("%lld\n",ans);
}
return 0;
}
根据题意,把边按从小到大的顺序加入结果显然是最优的。
什么时候会形成环?往最小生成树上加边就会形成环。
于是我们先构建一颗最小生成树。
然后从小到大加上非树边。
每加入一条非树边,就可以把路径间所有的点(环)缩成一个环。它们互相之间都有第二条联通路径相互到达,可视为一体。该操作用新并查集维护。
具体是将这些点的改变为的(最上面那个点)。
如果加上一条非树边边后,发现在同一并查集,就说明他们有了除最小生成树上路径以外的第二条联通路径,满足题意。这条非树边长度就是答案。
复杂度(并查集被看作常数复杂度)
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
#define re register
#define il inline
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=5e5+100;
int n,m,q,h[N],cnt,f[N],d[N],ff[N],vis[N];
ll num[N];
struct Edge
{
int to,nxt;ll w;
}e[N<<1];
struct dat
{
int u,v,vis;ll w;
bool operator < (const dat &o) const {return w<o.w;}
}a[N];
il void add(re int u,re int v,re ll w){e[++cnt].to=v;e[cnt].nxt=h[u];e[cnt].w=w;h[u]=cnt;}
il ll gi()
{
re ll x=0,t=1;
re char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') t=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*t;
}
il void wri(re int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) wri(x/10);
putchar(x%10+'0');
}
il int find(re int x){return x==f[x]?x:f[x]=find(f[x]);}
il void dfs(re int u)
{
vis[u]=1;
for(re int i=h[u];i+1;i=e[i].nxt)
{
re int v=e[i].to;
if(vis[v]) continue;
d[v]=d[u]+1;ff[v]=u;
dfs(v);
}
}
int main()
{
freopen("krap.in","r",stdin);
freopen("krap.out","w",stdout);
n=gi();m=gi();q=gi();
memset(h,-1,sizeof(h));
fp(i,1,n) num[i]=gi(),f[i]=i;
fp(i,1,m)
{
re int u=gi(),v=gi();ll w=num[u]-num[v];if(w<0) w=-w;
a[i].u=u,a[i].v=v,a[i].w=w;
}
sort(a+1,a+1+m);
fp(i,1,m)
{
re int u=a[i].u,v=a[i].v,w=a[i].w,fu=find(u),fv=find(v);
if(fu^fv)
{
a[i].vis=1;
f[fv]=fu;
add(u,v,w);add(v,u,w);
}
}
fp(i,1,n) if(!vis[i]) dfs(i);
while(q--)
{
re int uu=gi(),vv=gi();
re ll ans=-1;
fp(i,1,n) f[i]=i;
fp(i,1,m)
if(!a[i].vis)
{
re int u=a[i].u,v=a[i].v,fu=find(u),fv=find(v),pu=fu,pv=fv;
if(fu^fv)
{
while(fu^fv)
{
if(d[fu]<d[fv]) swap(fu,fv);
fu=ff[fu];
}
re int lca=fu;
for(;pu^lca;pu=find(ff[pu])) f[pu]=lca;
for(;pv^lca;pv=find(ff[pv])) f[pv]=lca;
if(find(uu)==find(vv)) {ans=a[i].w;break;}
}
}
ans==-1?puts("No way!"):printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}