一张图, k k k个特殊点。
必须在特殊点之间加一条边,但不确定是哪对,使得最短路最大。
首先,求出每个点到 1 1 1和到 n n n的最短路。
容易发现加边之后能得到的最短路为:
m i n ( d 1 [ a ] + 1 + d 2 [ b ] , d 1 [ b ] + 1 + d 2 [ a ] , d [ 1 ] [ n ] ) min(d_1[a]+1+d_2[b],d_1[b]+1+d_2[a],d[1][n]) min(d1[a]+1+d2[b],d1[b]+1+d2[a],d[1][n])
我们如何维护呢?首先我们不确定前两项的大小,这是关键。
昨晚在这里思路走歪了,虽然还是给我 A C AC AC了。
我列式子:
如果已知最短路 x x x,我肯定只要找到一对关键点,使得下面等式成立,
那么他们的最小值也会大于等于这个最短路,也就是说 x x x可以作为一个趋向于正确的解。也就是二分。
d 1 [ a ] + 1 + d 2 [ b ] > = x d_1[a]+1+d_2[b]>=x d1[a]+1+d2[b]>=x
d 1 [ b ] + 1 + d 2 [ a ] > = x d_1[b]+1+d_2[a]>=x d1[b]+1+d2[a]>=x
怎么快速找呢?
d 2 [ b ] > = x − 1 − d 1 [ a ] d_2[b]>=x-1-d_1[a] d2[b]>=x−1−d1[a]
d 1 [ b ] > = x − 1 − d 2 [ a ] d_1[b]>=x-1-d_2[a] d1[b]>=x−1−d2[a]
显然枚举 a a a,然后找到一个点满足这个式子,显然的二维线段树。
但是复杂度不成。
可以发现用离线树状数组可以,只需要注意到去除自己即可。
(如果自己和自己满足的话)
#include
#define FOR(i,l,r) for(int i=l;i<=r;i++)
using namespace std;
//typedef long long ll;
const int maxn=5e5+500,inf=0x3f3f3f3f;
const int maxm=1e6+600;
struct Edge{
int from,to;int dist;
Edge(){}
Edge(int _from,int _to,int _dist):from(_from),to(_to),dist(_dist){}
};
Edge ed[maxm];int he[maxn],ne[maxm],etop=1;
void insert(int u,int v,int w){ed[etop]=Edge(u,v,w);ne[etop]=he[u];he[u]=etop++;}
struct node{
int s,t,id,who;
friend bool operator < (node a,node b){
if(a.s==b.s&&a.t==b.t)return a.id>b.id;
if(a.s==b.s)return a.t<b.t;
return a.s<b.s;
}
}A[maxn],B[maxn];
int n,m,k;
int a[maxn];
int d[maxn];
bool vis[maxn];
priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > q;
void dijkstra(int s){
memset(vis,0,sizeof(vis));
memset(d, 0x3f, sizeof(d));
d[s] = 0;
q.push(make_pair(0, s));
while(!q.empty()){
int now = q.top().second;q.pop();
if(!vis[now]){
vis[now] = true;
for(int i = he[now]; i; i = ne[i]){
Edge& e = ed[i];
if(d[e.to] > d[now] + e.dist){
d[e.to] = d[now] + e.dist;
q.push(make_pair(d[e.to], e.to));
}
}
}
}
}
int C[500050];
const int limit=500005;
int lowbit(int x){return (x&(-x));}
void change(int x,int d){for(int i=x;i<=limit;i+=lowbit(i)){C[i]+=d;}}
int query(int x){if(x<=0)return 0;int ret=0;for(int i=x;i;i-=lowbit(i)){ret+=C[i];}return ret;}
int suf[maxn];
bool check(int x){
memset(C,0,sizeof(C));
for(int i=0;i<=2*n+100;i++)suf[i]=0;
for(int i=1;i<=k;i++){
B[i].id=0;
B[i].s=A[i].s,B[i].t=A[i].t;
B[i+k].id=1;
B[i+k].s=x-B[i].t-1,B[i+k].t=x-B[i].s-1;
B[i].s+=n+1,B[i].t+=n+1,B[i+k].s+=n+1,B[i+k].t+=n+1;
B[i].who=i,B[i+k].who=i;
if(A[i].s+A[i].t+1>=x)suf[i]=1;
}
//for(int i=1;i<=2*k;i++)cout<
sort(B+1,B+1+2*k);
// for(int i=1;i<=2*k;i++)cout<
for(int i=2*k;i>=1;i--){
if(B[i].id){
int ret=query(limit)-query(B[i].t-1);
if(ret-suf[B[i].who])return true;
}
else change(B[i].t,1);
}
return false;
}
int main(){
cin>>n>>m>>k;
FOR(i,1,k)scanf("%d",&a[i]);
FOR(i,1,m){
int u,v;scanf("%d%d",&u,&v);
insert(u,v,1);
insert(v,u,1);
}
int len;
dijkstra(1);len=d[n];
FOR(i,1,k)A[i].s=d[a[i]];
dijkstra(n);
FOR(i,1,k)A[i].t=d[a[i]];
int l=0,r=len,ans;
// FOR(i,1,k)cout<
// cout<
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)){
ans=mid;
l=mid+1;
}
else r=mid-1;
}
cout<<ans<<endl;
}
然后就是正解。
实际上在最开始我们可以发现,怎么判断两者的大小呢 ? ? ?
d 1 [ a ] + d 2 [ b ] + 1 ≤ d 1 [ b ] + d 2 [ a ] + 1 d_1[a]+d_2[b]+1\leq d_1[b]+d_2[a]+1 d1[a]+d2[b]+1≤d1[b]+d2[a]+1
d 1 [ a ] − d 2 [ a ] ≤ d 1 [ b ] − d 2 [ b ] d_1[a]-d_2[a]\leq d_1[b]-d_2[b] d1[a]−d2[a]≤d1[b]−d2[b]
对于这样的 a 、 b a、b a、b,显然左边式子是最小的,对于某个 a a a,所有满足差值比他小的,都是取左式,所以维护前缀最大值即可。
当然,对于一个点对,总是一个大一个小,只考虑大的那个,维护小的最大值即可。
int len;
dijkstra(1);len=d[n];
FOR(i,1,k)A[i].s=d[a[i]];
dijkstra(n);
FOR(i,1,k)A[i].t=d[a[i]];
sort(A+1,A+1+k);
int ans=0,mx=A[1].s;
for(int i=2;i<=k;i++){
ans=max(ans,mx+A[i].t+1);
mx=max(mx,A[i].s);
}
ans=min(ans,len);
cout<<ans<<endl;