【bzoj4603】【SDOI2016】【平凡的骰子】【计算几何】

Description

这是一枚平凡的骰子。它是一个均质凸多面体,表面有n个端点,有f个面,每一面是一个凸多边形,且任意两面不
共面。将这枚骰子抛向空中,骰子落地的时候不会发生二次弹跳(这是一种非常理想的情况)。你希望知道最终每
一面着地的概率。每一面着地的概率可以用如下的方法计算:我们假设O为骰子的重心,并以O为球心,做半径为1
的单位球面(记为S)。我们知道S的表面积即单位球的表面积,为4*pi,这里pi为圆周率。对于骰子的某一面C来
说,球面S上存在一块区域T满足:当下落时若骰子所受重力方向与S的交点落在T中,则C就是最终着地的一面。那
么C着地的概率为区域T的面积除以4*pi。为了能更好地辅助计算球面上一块区域的面积,我们给出单位球面S上三
角形的面积计算公式。考虑单位球面S上的三个两两相交的大圆,交点依次为A,B和C。则曲面三角形ABC的面积为A
rea(ABC)=alpha+beta+gamma-pi,其中alpha,beta和gamma分别对应了三个二面角的大小。如下图所示。
 
我们保证:每一面着地的时候,重心的垂心都恰好在这一面内。也就是说不会出现摆不稳的情况。

Input

第一行输入两个整数,分别表示端点总数n与表面总数f,分别从1开始编号。之后n行,每行有三个浮点数x,y和z
,给出了每一个端点的坐标。之后f行依次描述了每一块表面,首先给出不小于3的整数d,表示这一面的端点个数
,之后d个整数按照逆时针方向(视角在骰子的外面)给出了每一个端点的编号。
4<=n<=50且4<=m<=50,所有坐标的绝对值都在10000以内

Output

输出f行,第i行有一个浮点数,表示第i个面着地的概率。本题中您的输出应该保留距离答案最近的7位小数,即在
需要保留7位小数的前提之下与标准答案最接近。数据保证可以避免对小数点后第八位四舍五入后产生的精度误差

Sample Input

8 6
1 0 0
1 1 0
1 0 1
1 1 1
0 0 0
0 1 0
0 0 1
0 1 1
4 1 2 4 3
4 2 6 8 4
4 6 5 7 8
4 5 1 3 7
4 3 4 8 7
4 1 5 6 2

Sample Output

0.1666667
0.1666667
0.1666667
0.1666667
0.1666667
0.1666667
题解:
可以发现题目分为两步.
1.求重心.2.算球面多边形面积.
求重心我们可以对整个多面体进行四面体剖分.
求出每个四面体的重心.然后再按它们的体积加权平均.
求四面体的体积可以用一个点引出的三个向量的混合积乘1/6.
然后重心就求完了.
算面积直接按题目说的方法算就可以.
求二面角可以用法向量.
代码还是不难写的。。。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define pi 4.0*atan(1.0)
#define N 110
using namespace std;
struct Node{double x,y,z;}node[N],cen;
struct Surface{
  int num;
  Node node[N];
}surf[N];
int n,m,x;
Node operator+(Node a,Node b){
  Node c;
  c.x=a.x+b.x;
  c.y=a.y+b.y;
  c.z=a.z+b.z;
  return c;
}
Node operator-(Node a,Node b){
  Node c;
  c.x=a.x-b.x;
  c.y=a.y-b.y;
  c.z=a.z-b.z;
  return c;
}
Node operator*(Node a,double b){
  Node c;
  c.x=a.x*b;
  c.y=a.y*b;
  c.z=a.z*b;
  return c;
}
Node operator/(Node a,double b){
  Node c;
  c.x=a.x/b;
  c.y=a.y/b;
  c.z=a.z/b;
  return c;
}
double operator&(Node a,Node b){
  double ans=0.0;
  ans+=a.x*b.x;
  ans+=a.y*b.y;
  ans+=a.z*b.z;
  return ans;
}
Node operator|(Node a,Node b){
  Node c;
  c.x=a.y*b.z-a.z*b.y;
  c.y=a.z*b.x-a.x*b.z;
  c.z=a.x*b.y-a.y*b.x;
  return c;
}
double dis(Node a){
  double ans;
  ans=a.x*a.x+a.y*a.y+a.z*a.z;
  ans=sqrt(ans);
  return ans;
}
void init(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) scanf("%lf%lf%lf",&node[i].x,&node[i].y,&node[i].z);
  for (int i=1;i<=m;i++){
    scanf("%d",&surf[i].num);
    for (int j=0;j<surf[i].num;j++){
      scanf("%d",&x);
      surf[i].node[j]=node[x];
    }
  }
}
void getcenter(){
  for (int i=1;i<=n;i++)
    cen=cen+node[i];
  cen=cen/(double)n;    
}
double getv(Node p,Node x,Node y,Node z){
  x=x-p;y=y-p;z=z-p;
  double ans=(x|y)&z;
  ans=ans/6.0;
  if (ans<0) ans=-ans;
  return ans;
}
Node getc(Node p,Node x,Node y,Node z){
  Node c;
  c=p+x+y+z;
  c=c/4.0;
  return c;
}
void changecenter(){
  double sv=0.0;Node t;
  t.x=t.y=t.z=0.0;
  for (int i=1;i<=m;i++){
    int cc=surf[i].num;
    for (int j=1;j<=cc-2;j++){
      double v=getv(cen,surf[i].node[0],surf[i].node[j],surf[i].node[j+1]);
      Node tt=getc(cen,surf[i].node[0],surf[i].node[j],surf[i].node[j+1]);
      tt=tt*v;sv+=v;t=t+tt;
    }
  }
  cen=t/sv;
}
double getangle(Node p,Node x,Node y,Node z){
   x=x-p;y=y-p;z=z-p;
   x=x/dis(x);y=y/dis(x);z=z/dis(x);
   Node a=x|y,b=x|z;
   double ans=(a&b)/dis(a)/dis(b);
   ans=acos(ans);
   return ans;
}
double getsq(Surface &x,Node y){
  double ans=-(x.num-2)*pi;
  for (int i=0;i<x.num;i++)
    ans+=getangle(y,x.node[i],x.node[(i+1)%x.num],x.node[(i-1+x.num)%x.num]);
  return ans;
}
void getans(){
  for (int i=1;i<=m;i++){
    double ans=getsq(surf[i],cen);
    ans/=4.0*pi;
    printf("%.7lf\n",ans);
  }
}
int main(){
  init();   
  getcenter();
  changecenter();
  getans();
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值