题目
题目链接:https://codeforces.com/contest/1550/problem/F
数轴上顺次有 (n) 个点 (a_1 < a_2 < cdots < a_n)。
有一只小青蛙,初始时在 (a_s) 处。小青蛙有两个参数:步长 (d) 和灵活程度 (k)。其中,步长 (d) 是确定的,而灵活程度 (k) 是可以调整的。
小青蛙可以从某个点跳到另一个点。但这是有要求的:小青蛙能从 (a_i) 跳到 (a_j),当且仅当 (d-kleq |a_i-a_j|leq d+k)。
给定 (a_1,...,a_n) 和 (d)。你需要回答 (q) 次询问,每次询问给定一个灵活程度 (k) 和一个下标 (i),你需要回答:此时的小青蛙能否跳到 (a_i)?
(1leq n,qleq 2 imes 10^5),(1leq s,ileq n),(1leq a_i,d,kleq 10^6)。
思路
有一个显然的做法:对于 (i,j) 两点,在他们之间连权值为 (||a_i-a_j|-d|) 的边,然后跑最小生成树。那么一个点 (i) 最小需要的 (k) 就是最小生成树上 (s) 到 (i) 的边中权值的最大值。
但是这样边数是 (O(n^2)) 的。prim 和 kruskal 都不好搞。考虑一度被认定没用的 boruvka 算法。
boruvka 算法的流程如下:
- 一开始所有点各自为一个连通块。
- 对于每一个连通块,找到另一端不在该连通块的最小边权的边。
- 连接所有找到的边。
- 如果此时所有点连通则退出。否则回到步骤 (2)。
正确性比较显然,而如果此时有 (k) 个连通块,就至少会合并 (frac{k}{2}) 个连通块,所以时间复杂度为 (O((n+m)log n))。
发现这个过程中复杂度与边权有关的地方只有步骤 (2) 中需要枚举所有的边。但是这道题中的边权是有性质的。
维护一个 set,把所有 (a_i) 扔进去。步骤 (2) 中,对于一个连通块,我们枚举连通块内的所有点,把他们从 set 中删除。然后再次枚举连通块的所有点 (i),为了找到边权最小的边,其实就是需要在 set 中找到距离 (a_i+d) 或 (a_i-d) 最近的点。因为已经把连通块内的点在 set 中删除了,所以可以直接在 set 上二分 (4) 次找到。最后再把所有点加回来即可。
这样每进行一次步骤 (2),我们找的边数是 (O(nlog n)) 的。时间复杂度 (O(nlog^2 n))。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=200010,M=1000010,Inf=1e9;
int n,m,Q,rt,d,tot,head[N],a[N],ans[N],val[N],father[N],id[M];
set<int> s;
vector<int> b[N];
struct edge
{
int next,to,dis;
}e[N*2];
struct edge1
{
int u,v,dis;
}e1[N];
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
int getd(int x,int y)
{
return abs(abs(x-y)-d);
}
void check(int &u,int &v,int x,int y)
{
if (y==Inf || y==-Inf) return;
if (getd(x,y)<getd(u,v)) u=x,v=y;
}
void add(int from,int to,int dis)
{
e[++tot]=(edge){head[from],to,dis};
head[from]=tot;
}
void boruvka()
{
m=n;
for (int k=1;k<n;)
{
for (int i=1;i<=n;i++) b[i].clear();
for (int i=1;i<=n;i++) b[find(i)].push_back(i);
int cnt=0;
for (int i=1;i<=n;i++)
if (find(i)==i)
{
int u=0,v=Inf;
for (int j=0;j<b[i].size();j++)
s.erase(s.find(a[b[i][j]]));
for (int j=0;j<b[i].size();j++)
{
check(u,v,a[b[i][j]],*s.lower_bound(a[b[i][j]]+d));
check(u,v,a[b[i][j]],*(--s.lower_bound(a[b[i][j]]+d)));
check(u,v,a[b[i][j]],*s.lower_bound(a[b[i][j]]-d));
check(u,v,a[b[i][j]],*(--s.lower_bound(a[b[i][j]]-d)));
}
if (u) e1[++cnt]=(edge1){id[u],id[v],getd(u,v)};
for (int j=0;j<b[i].size();j++)
s.insert(a[b[i][j]]);
}
for (int i=1;i<=cnt;i++)
if (find(e1[i].u)!=find(e1[i].v))
{
k++;
int u=e1[i].u,v=e1[i].v,dis=e1[i].dis;
add(u,v,dis); add(v,u,dis);
father[find(u)]=find(v);
}
}
}
void dfs(int x,int fa,int mx)
{
ans[x]=mx;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs(v,x,max(mx,e[i].dis));
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d%d",&n,&Q,&rt,&d);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s.insert(a[i]); b[i].push_back(i);
father[i]=i; id[a[i]]=i;
}
s.insert(-Inf); s.insert(Inf);
boruvka();
dfs(rt,0,0);
while (Q--)
{
int x,y;
scanf("%d%d",&x,&y);
if (ans[x]<=y) cout<<"Yes
";
else cout<<"No
";
}
return 0;
}