例题6.18 绿色的世界 UVa11017

1.题目描述:点击打开链接

2.解题思路:本题利用Pick定理解决。Pick定理讲的是:给定一个顶点都是整点的简单多边形,其面积A,内部格点的数目I与边上的格点数目B的关系是:A=I+B/2-1。这样,我们就能算出I=A-B/2+1。不过,这样并不能完整的解决本题,因为题目中要求计算每个小菱形的中点有多少个。由于错切变换不会影响内部格点的个数,因此我们不妨仍然理解为在相互垂直的x-y坐标系中,那么等价于要我们找内部x,y坐标的小数部分都是0.5的点有多少个。此时我们可以想办法把它们也都变成“整点”。最直观的办法就是将坐标系顺时针旋转45度,这样,原来的整点还是整点,小数部分均为0.5的点也成了整点。因此,我们可以统计在原坐标系下的内部整点数,再统计坐标系旋转后的内部整点数,后者减去前者就是答案。

3.代码:

#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define me(s)  memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long LL;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;



const double PI = acos(-1.0);

struct Point 
{
  int x, y;
  Point(int x=0, int y=0):x(x),y(y) { }
};

typedef Point Vector;

Vector operator + (const Vector& A, const Vector& B) { return Vector(A.x+B.x, A.y+B.y); }
Vector operator - (const Point& A, const Point& B) { return Vector(A.x-B.x, A.y-B.y); }
double Cross(const Vector& A, const Vector& B) { return (LL)A.x*B.y - (LL)A.y*B.x; }

LL PolygonArea2(const vector<Point>& p) //计算多边形面积*2的结果
{
  int n = p.size();
  LL area2 = 0;
  for(int i = 1; i < n-1; i++)
    area2 += Cross(p[i]-p[0], p[i+1]-p[0]);
  return abs(area2);
}

inline int gcd(int a, int b) 
{
  return b == 0 ? a : gcd(b, a%b);
}

LL count_on_segment(const Point& a, const Point& b)//计算线段a-b上整点的个数,如果不计算端点,就是gcd(dx,dy)-1个,这不难通过参数表达式得到
{
  return gcd(abs(b.x-a.x), abs(b.y-a.y)) - 1;
}


LL count_inside_polygon(const vector<Point>& poly) //利用Pick定理可得:I=A-B/2+1
{
  int n = poly.size();
  LL A2 = PolygonArea2(poly);
  int B = n;
  for(int i = 0; i < n; i++)
    B += count_on_segment(poly[i], poly[(i+1)%n]);
  return (A2 - B) / 2 + 1;//注意:这里的A2是多边形面积*2的结果,因此也要除以2
}


LL count(const vector<Point>& poly) //查找x,y小数部分都是0.5的点
{
  vector<Point> poly2;
  for(int i = 0; i < poly.size(); i++)
    poly2.push_back(Point(poly[i].x-poly[i].y, poly[i].x+poly[i].y));
  return count_inside_polygon(poly2) - count_inside_polygon(poly);  //相减就是答案
}

int main() 
{
  int d, theta, N, x, y;   //这里给定的d,theta只是用来计算面积的,错切变换后相当于x-y坐标系下的面积乘以系数d*d*sin(theta)
  while(scanf("%d%d%d", &d, &theta, &N) == 3 && d) 
    {
        vector<Point> poly;
        for(int i = 0; i < N; i++) {
            scanf("%d%d", &x, &y);
        poly.push_back(Point(x, y));
    }
    LL area2 = PolygonArea2(poly);
    printf("%lld %.0lf\n", count(poly), sin((double)theta / 180 * PI) * d * d * area2 / 2.0);//area2/2才是原坐标系的面积
  }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值