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, ...
随机推荐
- PADS软件
最近学习PADS,搜集到的一些软件.之前一直在使用Altium designer,但是AD太占资源了,还有都说PADS比AD好. 下面是来自网上对主流PCB的介绍(原文:http://9mcu.com ...
- cmath模块——复数域数学函数模块
cmath——复数域数学函数模块 转自:https://blog.csdn.net/zhtysw/article/category/7511293 该模块属于内置模块,随时可以调用.它提供了数学函数在 ...
- Webhook到底是个啥?
参照: https://segmentfault.com/a/1190000015437514
- 【OpenCV】 在CentOS下搭建OpenCV开发环境
最近开始入模式识别的坑,自然被迫上OpenCV了. 在多次尝试给VS2015扩展Windows 10 SDK无果后(不要问我为啥..VS2015开发C++的标准库全给扔到这个SDK里了,打包在VS安装 ...
- 【python】 判断纯ascii串
参考:http://stackoverflow.com/questions/3636928/test-if-a-python-string-is-printable print all(ord(c)& ...
- SQLServer2008查询时对象名无效
情况一:如果表名是关键字,查询时把表名括起来,不作为关键字使用 情况二:看左上角显示的是否是master,这是数据库的默认系统库,点选这个改成自己的即可
- JSP基础--三大指令
JSP指令 1 JSP指令概述 JSP指令的格式:<%@指令名 attr1=”” attr2=”” %>,一般都会把JSP指令放到JSP文件的最上方,但这不是必须的. JSP中 ...
- servlet--三大域
requset \ session servletContext application
- spring-第十七篇之spring AOP基于注解的零配置方式
1.基于注解的零配置方式 Aspect允许使用注解定义切面.切入点和增强处理,spring框架可以识别并根据这些注解来生成AOP代理.spring只是用了和AspectJ 5一样的注解,但并没有使用A ...
- CentOS安装Prolog和Erlang语言
安装Erlang比较简单 下载Erlang的压缩包 输入tar -zxvf 压缩包 解压 进入解压的目录下 输入./configure 在./configure执行完成后,输入make 然后输入mak ...