2017 山东一轮集训 Day2 Shadow (三维凸包点在面上投影)
在三维坐标中,给定一个点光源,一个凸多面体,以及一个平面作为地面。
求该凸多面体在地面上阴影的面积。
这三个点共同确定了一个平面,这个平面就是地面。保证这三个点坐标互异且不共线。前三行每行三个实数,每行表示一个点。这三个点共同确定了一个平面,这个平面就是地面。保证这三个点坐标互异且不共线。
接下来一行三个实数,表示一个点。这个点就是点光源。
之后一个整数n,表示凸多面体顶点的数量。
之后n行,每行三个实数,表示凸多面体的一个顶点。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <utility> #ifdef DEBUG
const int MAXN = ;
#else
const int MAXN = ;
#endif const double eps = 1e-; using namespace std; struct Vector {
double x, y, z;
Vector(double _x = , double _y = , double _z = ): x(_x), y(_y), z(_z) {}
Vector operator+(const Vector &rhs) const {
return Vector(x + rhs.x, y + rhs.y, z + rhs.z);
}
Vector operator-(const Vector &rhs) const {
return Vector(x - rhs.x, y - rhs.y, z - rhs.z);
}
Vector operator*(double k) {
return Vector(x * k, y * k, z * k);
}
double len() {
return sqrt(x * x + y * y + z * z);
}
};
typedef Vector Point; inline double dot(const Vector &a, const Vector &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
} inline Vector cross(const Vector &a, const Vector &b) {
return Vector(a.y * b.z - b.y * a.z, a.z * b.x - b.z * a.x, a.x * b.y - a.y * b.x);
} bool flag;
inline Vector readv() {
double x, y, z;
scanf("%lf%lf%lf", &x, &y, &z);
if (flag) swap(y, z);
return Vector(x, y, z);
} inline void printv(const Vector &a) {
printf("(%lf, %lf, %lf)\n", a.x, a.y, a.z);
} struct Line {
Point s;
Vector d;
Line() {}
Line(Point _s, Point _t): s(_s), d(_t - _s) {}
}; struct Plain {
Point s;
Vector d;
Plain() {}
Plain(Point a, Point b, Point c): s(a), d(cross(b - a, c - a)) {}
} p;
Point s;
int n;
Point pol[MAXN];
Point sd[MAXN]; inline int dcmp(double x) {
return x < -eps ? - : x > eps ? : ;
} inline Point itsct(Line l, Plain p) {
double s1 = dot(p.d, l.s - p.s);
double s2 = dot(p.d, l.s + l.d - p.s);
if (!dcmp(s1 - s2)) {
return itsct(Line(l.s, l.s + p.d), p);
}
double k = s1 / (s1 - s2);
return l.s + l.d * k;
} bool cmp(const Point &a, const Point &b) {
return a.x < b.x;
} Point conv[MAXN];
inline int make_convex() {
int cnt = ;
sort(sd, sd + n, cmp);
conv[cnt++] = sd[];
for (int i = ; i < n; i++) {
while (cnt > && dcmp(cross(sd[i] - conv[cnt - ], conv[cnt - ] - conv[cnt - ]).z) < ) cnt--;
if (dcmp((conv[cnt - ] - sd[i]).len())) conv[cnt++] = sd[i];
}
for (int i = n - ; i >= ; i--) {
while (cnt > && dcmp(cross(sd[i] - conv[cnt - ], conv[cnt - ] - conv[cnt - ]).z) < ) cnt--;
if (dcmp((conv[cnt - ] - sd[i]).len())) conv[cnt++] = sd[i];
}
return cnt;
} inline void open_file() {
freopen("shadow.in", "r", stdin);
freopen("shadow.out", "w", stdout);
} int main() {
//open_file();
Point a = readv(), b = readv(), c = readv();
if (!dcmp(cross(b - a, c - a).z)) {
swap(a.y, a.z), swap(b.y, b.z), swap(c.y, c.z);
flag = true;
}
p = Plain(a, b, c);
s = readv();
scanf("%d", &n);
for (int i = ; i < n; i++) pol[i] = readv();
for (int i = ; i < n; i++) sd[i] = itsct(Line(s, pol[i]), p);
int cnt = make_convex();
double ans = ;
for (int i = ; i < cnt - ; i++) {
ans += cross(conv[i] - conv[], conv[i + ] - conv[]).len();
}
printf("%.2lf\n", ans / ); return ;
}
直接给你平面方程
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath> using namespace std; typedef struct p1node
{
double a,b,c,d;
}plane;
plane Pl; typedef struct p2node
{
double x,y,z;
}point;
point temp;
point P[ ];
point S[ ]; //计算点在平面上的投影
int shadow( plane p, point s, int n )
{
//求出过s平行于plane的平面 ax+by+c=D
double D = p.a*s.x+p.b*s.y+p.c*s.z;
if ( D-p.d < ) {//调整方向
p.a *= -;p.b *= -;p.c *= -;
p.d *= -;
D *= -;
} //判断点与面的关系
int count = ;
for ( int i = ; i < n ; ++ i ) {
double det = p.a*P[i].x+p.b*P[i].y+p.c*P[i].z-D;
if ( det < ) count ++;
}
if ( count == ) return ;
if ( count != n ) return n+; for ( int i = ; i < n ; ++ i ) {
//直线方程: (Sx,Sy,Sz) + t(dx,dy,dz)
double dx = P[i].x - s.x;
double dy = P[i].y - s.y;
double dz = P[i].z - s.z;
double t = (p.d-p.a*s.x-p.b*s.y-p.c*s.z)/(p.a*dx+p.b*dy+p.c*dz); P[i].x = s.x + t*dx;
P[i].y = s.y + t*dy;
P[i].z = s.z + t*dz;
}
return n;
} //坐标系旋转,到x-y平面
void change( plane p, int n )
{
//平行于x-y平面的不用计算,也不能计算(分母为0)
if ( p.a*p.a + p.b*p.b == ) return;
for ( int i = ; i < n ; ++ i ) {
//绕z轴旋转
double cosC = p.b/sqrt(p.a*p.a+p.b*p.b);
double sinC = p.a/sqrt(p.a*p.a+p.b*p.b);
temp.x = P[i].x*cosC-P[i].y*sinC;
temp.y = P[i].x*sinC+P[i].y*cosC;
temp.z = P[i].z;
P[i] = temp;
//绕x轴旋转
double cosA = p.c/sqrt(p.a*p.a+p.b*p.b+p.c*p.c);
double sinA = sqrt(p.a*p.a+p.b*p.b)/sqrt(p.a*p.a+p.b*p.b+p.c*p.c);
temp.x = P[i].x;
temp.y = P[i].y*cosA-P[i].z*sinA;
temp.z = P[i].y*sinA+P[i].z*cosA;
P[i] = temp;
}
} //计算二维凸包面积
double crossproduct( point a, point b, point c )
{
return (b.x-a.x)*(c.y-a.y)-(c.x-a.x)*(b.y-a.y);
} bool cmp1( point a, point b )
{
if ( a.x == b.x )
return a.y < b.y;
return a.x < b.x;
} bool cmp2( point a, point b )
{
return crossproduct( P[], a, b )>;
} void Graham( int n )
{
sort( P+, P+n, cmp1 );
sort( P+, P+n, cmp2 ); int top = -;
if ( n > ) S[++ top] = P[];
if ( n > ) S[++ top] = P[];
if ( n > ) {
for ( int i = ; i < n ; ++ i ) {
while ( crossproduct( S[top-], S[top], P[i] ) < ) -- top;
S[++ top] = P[i];
}
} double area = 0.0;
for ( int i = ; i <= top ; ++ i )
area += crossproduct( S[], S[i-], S[i] ); printf("%.2lf\n",area*0.5);
} int main()
{
int n;
while ( cin >> Pl.a >> Pl.b >> Pl.c >> Pl.d ) {
if ( Pl.a == && Pl.b == && Pl.c == ) break;
cin >> n;
for ( int i = ; i <= n ; ++ i )
cin >> P[i].x >> P[i].y >> P[i].z; int s_count = shadow( Pl, P[n], n );
if ( !s_count )
cout << "0.00" << endl;
else if ( s_count > n )
cout << "Infi" << endl;
else {
change( Pl, s_count );
Graham( s_count );
}
}
}
2017 山东一轮集训 Day2 Shadow (三维凸包点在面上投影)的更多相关文章
- LOJ #6062. 「2017 山东一轮集训 Day2」Pair
这是Lowest JN dalao昨天上课讲的一道神题其实是水题啦 题意很简单,我们也很容易建模转化出一个奇怪的东西 首先我们对b进行sort,然后我们就可以通过二分来判断出这个数可以和哪些数配对 然 ...
- loj#6062. 「2017 山东一轮集训 Day2」Pair hall定理+线段树
题意:给出一个长度为 n的数列 a和一个长度为 m 的数列 b,求 a有多少个长度为 m的连续子数列能与 b匹配.两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当 ...
- 【LOJ6062】「2017 山东一轮集训 Day2」Pair(线段树套路题)
点此看题面 大致题意: 给出一个长度为\(n\)的数列\(a\)和一个长度为\(m\)的数列\(b\),求\(a\)有多少个长度为\(m\)的子串与\(b\)匹配.数列匹配指存在一种方案使两个数列中的 ...
- ACM-ICPC 2017 西安赛区现场赛 K. LOVER II && LibreOJ#6062. 「2017 山东一轮集训 Day2」Pair(线段树)
题目链接:西安:https://nanti.jisuanke.com/t/20759 (计蒜客的数据应该有误,题目和 LOJ 的大同小异,题解以 LOJ 为准) LOJ:https://l ...
- LOJ6062「2017 山东一轮集训 Day2」Pair(Hall定理,线段树)
题面 给出一个长度为 n n n 的数列 { a i } \{a_i\} {ai} 和一个长度为 m m m 的数列 { b i } \{b_i\} {bi},求 { a i } \{a_i\} ...
- 【LOJ6067】【2017 山东一轮集训 Day3】第三题 FFT
[LOJ6067][2017 山东一轮集训 Day3]第三题 FFT 题目大意 给你 \(n,b,c,d,e,a_0,a_1,\ldots,a_{n-1}\),定义 \[ \begin{align} ...
- Loj #6069. 「2017 山东一轮集训 Day4」塔
Loj #6069. 「2017 山东一轮集训 Day4」塔 题目描述 现在有一条 $ [1, l] $ 的数轴,要在上面造 $ n $ 座塔,每座塔的坐标要两两不同,且为整点. 塔有编号,且每座塔都 ...
- Loj #6073.「2017 山东一轮集训 Day5」距离
Loj #6073.「2017 山东一轮集训 Day5」距离 Description 给定一棵 \(n\) 个点的边带权的树,以及一个排列$ p\(,有\)q $个询问,给定点 \(u, v, k\) ...
- Loj 6068. 「2017 山东一轮集训 Day4」棋盘
Loj 6068. 「2017 山东一轮集训 Day4」棋盘 题目描述 给定一个 $ n \times n $ 的棋盘,棋盘上每个位置要么为空要么为障碍.定义棋盘上两个位置 $ (x, y),(u, ...
随机推荐
- Add hatch to bar plot
function applyhatch(h,patterns,colorlist) %APPLYHATCH Apply hatched patterns to a figure % APPLYHATC ...
- Linux 两台服务器之间传递文件
参考: https://www.cnblogs.com/clovershell/p/9870603.html linux采用scp命令拷贝文件到本地,拷贝本地文件到远程服务器 // 假设远程服务器 ...
- SqlServer 高级查询
高级查询在数据库中用得是最频繁的,也是应用最广泛的. Ø 基本常用查询 --select select * from student; --all 查询所有 select all sex from ...
- Vulhub搭建
Vulhub是一个比较全面的漏洞集合,收集了近年来许多的重要漏洞,以开源的形式呈现,以docker的形式进行环境配置,提供了更简单的漏洞复现,只需要简单的命令即可实现漏洞复现. 官网 https:/ ...
- Ubuntu下面怎么连接drcom校园网?(重庆大学实测可行)
之前因为ubuntu下面不能连drcom接校园头疼了半天,我们学校自带的客户端成功运行了,但是还是不能上网.\ 于是我百度了半天,搜了一堆教程...因为技术太渣好多教程里面不会修改参数,然后都不能成功 ...
- 机器学习 coursera_ML
在开始看之前,浏览器一直出现缓冲问题,是配置文件设置的不对,最后搞定,高兴!解决方法如下: 1.到C:\Windows\System32\drivers\etc下找到host文件,并以文本方式打开, ...
- [Linux] 022 RPM 包查询
1. 查询是否安装 (1) 查询包是否安装 $ rpm -q 包名 选项 释义 -q (query) 查询 (2) 查询所有已安装的 RPM 包 $ rpm -qa 选项 释义 -a (all) 所有 ...
- 自定义ThreadLocal和事务(基于自定义AOP)
参考<架构探险--从零开始写javaweb框架>4.6章节 自定义ThreadLocal package smart; import java.util.Collections; impo ...
- JavaScript中函数带与不带括号的区别
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- traceroute学习
之前只知道ping telnet命令,后面学习了traceroute命令 ping最常用的,看是否可以ping通ip,查看网络是否可达 telnet探测端口是否通,telnet ip port tra ...