HDU 4305 Lightning(判断点在线段上+生成树计数)

本文介绍了一个算法问题,即计算平面中多个点形成的图的生成树个数,并提供了一段C++代码实现。该问题首先通过判断点之间的距离及是否存在共线点来构建图,然后利用矩阵运算计算生成树的数量。

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

题意:平面上有N<300个点。每个两个点如果距离小于R且之间没有共线的另一个点,则这两点之间有一条边。求这个图的生成树的个数mod 10007。

思路:首先建图,判断任意两点间有无其他点,没有的话连一条边,剩下的就是统计生成树的数量。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<ctime>
#define eps 1e-6
#define LL long long
#define pii pair<int, int>
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int MAXN = 350;
const int MOD = 10007;
//const int INF = 0x3f3f3f3f;
int n, r;
int inv[11000];
int pow_mod(int a, int p, int n) {
	if(p == 0) return 1;
	LL ans = pow_mod(a, p/2, n);
	ans = ans * ans % n;
	if(p%2 == 1) ans = ans * a % n;
	return ans;
} 
void init() {
	inv[0] = 1;
	for(int i = 1; i < MOD; i++) inv[i] = pow_mod(i, MOD-2, MOD);
}
struct Point {
	int x, y;
	Point(int x=0, int y=0) : x(x), y(y) {} 
} p[MAXN];
typedef Point Vector;
Vector operator + (Vector A, Vector B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (Point A, Point B) { return Vector(A.x-B.x, A.y-B.y); }
Vector operator * (Vector A, int p) { return Vector(A.x*p, A.y*p); }
int operator * (Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
Vector operator / (Vector A, int p) { return Vector(A.x/p, A.y/p); }
struct Line {
	Point s, e;
	Line() {}
	Line(Point _s, Point _e) {
		s = _s;
		e = _e;
	} 
}; 
//判断一个点是否在线段上 
bool onSeg(Point P, Line L) {
	return 
	((L.s-P)*(L.e-P)) == 0 &&
    (P.x-L.s.x)*(P.x-L.e.x) <= 0 &&
    (P.y-L.s.y)*(P.y-L.e.y) <= 0;
}
int get_dis(int i, int j) {
	return (p[i].x-p[j].x)*(p[i].x-p[j].x) + (p[i].y-p[j].y)*(p[i].y-p[j].y);
}
bool check(int a, int b) {
	if(get_dis(a, b) > r*r) return false;
	Line tmp = Line(p[a], p[b]);
	for(int k = 0; k < n; k++) if(k!=a && k!=b) {
		if(onSeg(p[k], tmp)) return false;
	}
	return true;
}
struct Matrix {
	int a[MAXN][MAXN];
	Matrix() {
		memset(a, 0, sizeof(a));
	}
	void clear() {
		memset(a, 0, sizeof(a));
	}
	Matrix operator + (const Matrix &b) const {
		Matrix tmp;
		for(int i = 0; i < n; i++)
			for(int j = 0; j < n; j++) 
				tmp.a[i][j] = a[i][j] + b.a[i][j];
		return tmp;
	}
	Matrix operator * (const Matrix& b) const {
		Matrix tmp;
		tmp.clear();
		for(int i = 0; i < n; i++) 
			for(int j = 0; j < n; j++) 
				for(int k = 0; k < n; k++) tmp.a[i][j] += a[i][k]*b.a[k][j];
		return tmp;
	}
	Matrix operator ^ (int k) {
		Matrix ans, tmp = *this;
		while(k) {
			if(k&1) ans = ans * tmp;
			k >>= 1;
			tmp = tmp * tmp;
		}
		return ans;
	}
	int det(int n)//求行列式的值模上MOD,需要使用逆元
    {
        for(int i = 0;i < n;i++)
            for(int j = 0;j < n;j++)
                a[i][j] = (a[i][j]%MOD+MOD)%MOD;
        int res = 1;
        for(int i = 0;i < n;i++)
        {
            for(int j = i;j < n;j++)
                if(a[j][i]!=0)
                {
                    for(int k = i;k < n;k++)
                        swap(a[i][k],a[j][k]);
                    if(i != j)
                        res = (-res+MOD)%MOD;
                    break;
                }
            if(a[i][i] == 0)
            {
                res = -1;//不存在(也就是行列式值为0)
                break;
            }
            for(int j = i+1;j < n;j++)
            {
                //int mut = (a[j][i]*INV[a[i][i]])%MOD;//打表逆元
                int mut = (a[j][i]*inv[a[i][i]])%MOD;
                for(int k = i;k < n;k++)
                    a[j][k] = (a[j][k]-(a[i][k]*mut)%MOD+MOD)%MOD;
            }
            res = (res * a[i][i])%MOD;
        }
        return res;
    }

}; 
Matrix C;
int main() {
    //freopen("input.txt", "r", stdin);
	int T; cin >> T;
	init();
	while(T--) {
		cin >> n >> r;
		C.clear();
		for(int i = 0, u, v; i < n; i++) {
			scanf("%d%d", &u, &v);
			p[i] = Point(u, v);
		}
		for(int i = 0; i < n; i++) {
			for(int j = i+1; j < n; j++) {
				if(check(i, j)) {
					C.a[i][j]--;
					C.a[j][i]--;
					C.a[i][i]++;
					C.a[j][j]++;
				}
			}
		}
		cout << C.det(n-1) << endl;
	}
    return 0;
}

















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值