在一个二维平面上有 n 个点和 m 个圆。
点的编号为 1∼n。
不存在某个点恰好在某个圆的边上的情况。
任意两个圆之间没有公共点。
现在,请你回答 k 个询问。
每个询问给定两个点 ai,bi,并请你回答从点 ai 出发沿任意连续路径到达点 bi,至少需要穿过多少个圆。
输入格式
第一行包含三个整数 n,m,k。
接下来 n 行,其中第 i 行包含两个整数 xi,yi,表示点 i 的坐标为 (xi,yi)。注意,点的位置可以重合。
再接下来 m 行,其中第 i 行包含三个整数 ri,cxi,cyi,表示第 i 个圆的半径为 ri,圆心坐标为 (cxi,cyi)。
最后 k 行,每行包含两个整数 ai,bi,表示一个询问。注意,ai 可以等于 bi。
输出格式
共 k 行,第 i 行输出第 i 个询问的答案,即最少需要穿过的圆的数量。
数据范围
前三个测试点满足 1≤n,m,k≤10。
所有测试点满足 1≤n,m≤1000,1≤k≤105,−109≤xi,yi,cxi,cyi≤109,1≤ri≤109,1≤ai,bi≤n。
输入样例1:
2 1 1
0 0
3 3
2 0 0
1 2
输出样例1:
1
输入样例2:
2 3 1
0 0
4 4
1 0 0
2 0 0
3 0 0
1 2
输出样例2:
3
bitset做法题解
遍历n个点,每个点对m个圆有在圆内和圆外两个状态,园内表示0,圆外表示1,把状态用二进制存储下来
如果a点,b点两个点,a在圆内b在圆外,或者a在圆外b在圆内,那么这个圆是一定需要穿过的
从a点到b点最少需要穿过的圆数等于,两个状态的异或后的1的数量
#include <iostream>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <cstdio>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1010;
int n,m,Q;
PII p[N],c[N];
int r[N];
bitset<N> st[N];
LL sqr(LL x){
return x*x;
}
int check(int a,int b)
{
LL dx=p[a].x-c[b].x;
LL dy=p[a].y-c[b].y;
if(sqr(dx)+sqr(dy)>sqr(r[b]))
return 1;
return 0;
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
for(int i=1; i<=n; ++i)
scanf("%d%d",&p[i].x,&p[i].y);
for(int i=0; i<m; ++i){
scanf("%d%d%d",&r[i],&c[i].x, &c[i].y);
}
//遍历n个点,每个点对m个圆有在圆内和圆外两个状态,园内表示0,圆外表示1,把状态用二进制存储下来
//如果a点b点两个点,a在圆内b在圆外,或者a在圆外b在圆内,那么这个圆是一定需要穿过的
//从a点到b点最少需要穿过的圆数等于,两个状态的异或后的1的数量
for(int i=1; i<=n; ++i)
for(int j=0; j<m; ++j){
st[i][j]=check(i,j);
}
while(Q--){
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",(st[a]^st[b]).count());
}
}
倍增求LCA解法 O(n2+klogn)
这个代码转载于原题解
#include <iostream>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1010, M = 10;
int n, m, Q;
PII point[N];
int h[N], e[N], ne[N], idx;
int q[N], depth[N], fa[N][M];
int bel[N];
struct Circle
{
int x, y, r;
}cir[N];
LL sqr(LL x)
{
return x * x;
}
bool check(PII p, Circle c)
{
LL dx = p.x - c.x;
LL dy = p.y - c.y;
if (sqr(dx) + sqr(dy) < sqr(c.r)) return true;
return false;
}
void add(int a, int b) // 添加一条边a->b
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void bfs(int root) // 预处理倍增数组
{
memset(depth, 0x3f, sizeof depth);
depth[0] = 0, depth[root] = 1; // depth存储节点所在层数
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
if (depth[j] > depth[t] + 1)
{
depth[j] = depth[t] + 1;
q[ ++ tt] = j;
fa[j][0] = t; // j的第二次幂个父节点
for (int k = 1; k <= 9; k ++ )
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a, int b) // 返回a和b的最近公共祖先
{
if (depth[a] < depth[b]) swap(a, b);
for (int k = 9; k >= 0; k -- )
if (depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if (a == b) return a;
for (int k = 9; k >= 0; k -- )
if (fa[a][k] != fa[b][k])
{
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
int main()
{
scanf("%d%d%d", &n, &m, &Q);
for (int i = 1; i <= n; i ++ ) scanf("%d%d", &point[i].x, &point[i].y);
for (int i = 1; i <= m; i ++ )
scanf("%d%d%d", &cir[i].r, &cir[i].x, &cir[i].y);
memset(h, -1, sizeof h);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (check(point[i], cir[j]))
if (!bel[i] || cir[bel[i]].r > cir[j].r)
bel[i] = j;
for (int i = 1; i <= n; i ++ )
if (!bel[i])
bel[i] = m + 1;
for (int i = 1; i <= m; i ++ )
{
int t = 0;
for (int j = 1; j <= m; j ++ )
if (cir[j].r > cir[i].r && check({cir[i].x, cir[i].y}, cir[j]))
if (!t || cir[t].r > cir[j].r)
t = j;
if (!t) add(m + 1, i);
else add(t, i);
}
bfs(m + 1);
while (Q -- )
{
int a, b;
scanf("%d%d", &a, &b);
a = bel[a], b = bel[b];
int p = lca(a, b);
printf("%d\n", depth[a] + depth[b] - depth[p] * 2);
}
return 0;
}
作者:yxc
链接:https://www.acwing.com/activity/content/code/content/2196548/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。