Codeforces Beta Round #33 - D. Knights (暴力//建图+lca//bitset状态压缩)

本文探讨了在二维平面上给定点集和圆形障碍物的情况下,如何高效计算任意两点间需要跨越多少个障碍物的问题。提供了三种解决方案:暴力法、状态压缩以及构建图结合最低公共祖先(LCA)算法的方法,并附带详细的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Knights
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Berland is facing dark times again. The army of evil lord Van de Mart is going to conquer the whole kingdom. To the council of war called by the Berland's king Valery the Severe came n knights. After long discussions it became clear that the kingdom has exactly n control points (if the enemy conquers at least one of these points, the war is lost) and each knight will occupy one of these points. 

Berland is divided into m + 1 regions with m fences, and the only way to get from one region to another is to climb over the fence. Each fence is a circle on a plane, no two fences have common points, and no control point is on the fence. You are given k pairs of numbers aibi. For each pair you have to find out: how many fences a knight from control point with index ai has to climb over to reach control point bi (in case when Van de Mart attacks control point bi first). As each knight rides a horse (it is very difficult to throw a horse over a fence), you are to find out for each pair the minimum amount of fences to climb over.

Input

The first input line contains three integers nmk (1 ≤ n, m ≤ 10000 ≤ k ≤ 100000). Then follow n lines, each containing two integers KxiKyi ( - 109 ≤ Kxi, Kyi ≤ 109) — coordinates of control point with index i. Control points can coincide.

Each of the following m lines describes fence with index i with three integers riCxiCyi (1 ≤ ri ≤ 109 - 109 ≤ Cxi, Cyi ≤ 109) — radius and center of the circle where the corresponding fence is situated.

Then follow k pairs of integers aibi (1 ≤ ai, bi ≤ n), each in a separate line — requests that you have to answer. ai and bi can coincide.

Output

Output exactly k lines, each containing one integer — the answer to the corresponding request.

Examples
input
Copy
2 1 1
0 0
3 3
2 0 0
1 2
output
1
input
Copy
2 3 1
0 0
4 4
1 0 0
2 0 0
3 0 0
1 2
output
3


题意:

给你几个圆,几个点,问你点与点之间隔了多少个圆。


POINT:

1.暴力: 500ms

记录下每个点在每个圆的外面还是里面, 点与点不一样就ans++。


2.状态压缩:180ms

差不多,只是用异或来寻求答案。

用bitset来存下1000位的01串。


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <bitset>
using namespace std;
#define LL long long
const LL maxn = 2e3 + 10;
LL n,m,k,x[maxn],y[maxn],z;
struct node{
	LL r,x,y;
}C[maxn];
bitset<1010> a[1111];



int main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	for(LL i=1;i<=n;i++){
		scanf("%lld%lld",&x[i],&y[i]);
		a[i].reset();
	}
	for(LL i=1;i<=m;i++){
		scanf("%lld%lld%lld",&C[i].r,&C[i].x,&C[i].y);
	}
	for(LL i=1;i<=n;i++){
		for(LL j=1;j<=m;j++){
			LL len = (x[i]-C[j].x)*(x[i]-C[j].x)+(y[i]-C[j].y)*(y[i]-C[j].y);
			if(len<=C[j].r*C[j].r){
				a[i][j]=1;
			}else{
				a[i][j]=0;
			}
		}
	}
	while(k--)
	{
		LL q,w;
		scanf("%lld %lld",&q,&w);
		printf("%lld\n",(LL)(a[q]^a[w]).count());
	}



	return 0;
}




3.建图+lca:250ms。

的确是250的做法。把每个圆和他的父圆连一条边。父圆(最近的包含他的圆)。这样就建了一颗树,根为无限大的圆。

然后每个点属于哪个圆。跑lca。


#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include <queue>
using namespace std;
#define LL long long
const LL maxn = 2e3 + 10;
LL n,m,k,x[maxn],y[maxn],z;
struct node{
	LL r,x,y;
}C[maxn];
bool cmp(node a,node b){
	return a.r<b.r;
}
LL check(LL i,LL j){
	LL len = (C[i].x-C[j].x)*(C[i].x-C[j].x) + (C[i].y-C[j].y)*(C[i].y-C[j].y);
	if(len<=C[j].r*C[j].r) return 1;
	return 0;
}
const LL N = 1e6 + 10;
LL v[N],ft[N],nt[N],sz = 0;
LL dian[N];
LL fa[N][20];
LL vis[N];
LL d[N];

void dfs(LL u)
{
    vis[u]=1;
    for(LL i=ft[u];i;i=nt[i]){
        LL to = v[i];
        if(vis[to]) continue;
        d[to]=d[u]+1;
        dfs(to);
        fa[to][0]=u;
    }
}
void lcainit()
{
    for(LL j=1;j<=(LL)log2(m+1);j++){
        for(LL i=0;i<=m;i++){
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
}


LL query(LL a,LL b)
{
    if(d[a]<d[b]) swap(a,b);
    LL xx=a;
    LL yy=b;
    LL dis=d[a]-d[b];
    for(LL i=0;i<=(LL)log2(m+1);i++){
        if((1<<i)&dis){
            a=fa[a][i];
        }
    }
    if(a==b){
        return dis;
    }
    for(LL i=(LL)log2(m+1);i>=0;i--){
        if(fa[a][i]!=fa[b][i]){
            a=fa[a][i];
            b=fa[b][i];
        }
    }
    a=fa[a][0];
    return d[xx]-d[a]+d[yy]-d[a];
}


int main()
{
	scanf("%lld%lld%lld",&n,&m,&k);
	for(LL i=0;i<n;i++){
		scanf("%lld%lld",&x[i],&y[i]);
	}
	for(LL i=0;i<m;i++){
		scanf("%lld%lld%lld",&C[i].r,&C[i].x,&C[i].y);
	}
	sort(C,C+m,cmp);
	for(LL i=0;i<m;i++){
		LL f = 1;
		for(LL j=i+1;j<m;j++){
			if(check(i,j)){
				v[++sz] = j; nt[sz] = ft[i]; ft[i] = sz;
				v[++sz] = i; nt[sz] = ft[j]; ft[j] = sz;
				f = 0;
				break;
			}
		}
		if(f){
			v[++sz] = m; nt[sz] = ft[i]; ft[i] = sz;
			v[++sz] = i; nt[sz] = ft[m]; ft[m] = sz;
		}
	}
	for(LL i=0;i<n;i++){
		LL f = 1;
		for(LL j=0;j<m;j++){
			LL len = (x[i]-C[j].x)*(x[i]-C[j].x)+(y[i]-C[j].y)*(y[i]-C[j].y);
			if(len<=C[j].r*C[j].r){
				dian[i] = j;
				f = 0;
				break;
			}
		}
		if(f) dian[i] = m;
	}

  /*  cout<<endl;cout<<endl;cout<<endl;
	for(int i=0;i<n;i++) cout<<dian[i]<<" ";
	cout<<endl;
	for(int i=0;i<=m;i++){
		cout<<i<<"  ";
		for(int j=ft[i];j;j=nt[j]){
			cout<<v[j]<<" ";
		}
		cout<<endl;
	}
cout<<endl;cout<<endl;cout<<endl;*/
	dfs(m);
	lcainit();
	while(k--)
    {
        LL a,b;
        scanf("%lld %lld",&a,&b);
        printf("%lld\n",query(dian[a-1],dian[b-1]));
    }

    return 0;
}



ps:

cf很强大,一秒可以跑好几亿


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值