羅区3829:[SHOI2012]クレジットカードの凸包
タイトル説明
- 矩形があり、四隅には、それらは、矩形の辺に対して接線方向であるように、滑らかな取り扱いをした\(\ FRAC {1} { 4} \) 円。これと同じ平面長方形の大きさのいくつか、凸包の周囲を見つけることがあります。
入力形式
- 最初の行は、正の整数を入力し\(N- \)は、矩形の数を表します。2行目は、3つの実数値が得られる\(A、B、Rを\ ) の矩形の水平+垂直方向の長さを表し、\(\ FRAC {1} { 4} \) 円の半径。
- 後\(N- \)列、三列パック当たりの実\(X、Y、θは\)は、水平および垂直座標と回転の中心の周りに反時計方向の円弧の矩形(対角線の交点)の中心を表しています。
出力フォーマット
- 出力は、最も近い小数点以下を四捨五入凸包周囲の実数を表し、\(2 \)ビット。
データ範囲
- \(1 \当量のn \当量10 ^ 5 \)
思考
- それは四隅が問題を扱う滑らかしなかった場合は、その後です\(R = 0 \)の場合、それははるかに容易になります、長方形の四隅にはグラハムを実行することができた後、直接抽出。
- しかし、実際の後、近い検査が一緒になって、いくつかの弧状の場所が矩形の滑らかな取り扱いのために言うことである、360度の角度に等しい明らかに、最終的な結果は、凸丸みを帯びた矩形と等価であるから成る処理しませんパケットプラス、円の周囲長。
- 図に示すように。
- 等価プラス最外周週の赤い部分。
そこで問題は、2次元凸包裸のタイトルになります。
- どのようにする処理の中心の周りに回転
- 假设原先没旋转前,中心点为\((x,y)\),右上角的点是\((x_0,y_0)\),矩形绕中心旋转\(theta\),右上角的点变成了\((x_1,y_1)\)。假设对角线长度的一半为\(length\)。\(a\)为矩形竖直方向的长度的一半,\(b\)为矩形水平方向的长度的一半。
- 首先可以求得\(β=asin(\frac{a}{length})\)。
- 可以发现,旋转之后\((x_1,y_1)=(x+length*cos(β+θ),y+length*sin(β+θ))\)
- 剩下三个点同理,推一推就能明白了,这里给出公式:
- 右上角:\((x+length*cos(β+θ),y+length*sin(β+θ))\)
- 右下角:\((x+length*cos(β-θ),y-length*sin(β-θ))\)
- 左上角:\(((x-length*cos(β-θ),y+length*sin(β-θ)))\)
- 左下角:\((x-length*cos(β+θ),y-length*sin(β+θ))\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
const double PI = acos(-1);
int n, cnt, tot;
double a, b, r, x, y, theta, beta, length;
struct Node{
double x, y;
}p[maxn<<2], s[maxn<<2];
Node get_point(int op, double theta)
{
Node a;
if(op == 1) //右上角
a.x = x+length*cos(beta+theta), a.y = y+length*sin(beta+theta);
if(op == 2) //右下角
a.x = x+length*cos(beta-theta), a.y = y-length*sin(beta-theta);
if(op == 3) //左上角
a.x = x-length*cos(beta-theta), a.y = y+length*sin(beta-theta);
if(op == 4) //左下角
a.x = x-length*cos(beta+theta), a.y = y-length*sin(beta+theta);
return a;
}
double vp(Node a1, Node a2, Node b1, Node b2){
return (a2.x-a1.x)*(b2.y-b1.y) - (b2.x-b1.x)*(a2.y-a1.y);
}
double dis(Node a, Node b){
return sqrt((b.y-a.y)*(b.y-a.y)+(b.x-a.x)*(b.x-a.x));
}
bool cmp(Node a, Node b)
{
double tmp = vp(p[1], a, p[1], b);
if(tmp > 0) return 1;
if(tmp == 0 && dis(p[0], a) < dis(p[0], b)) return 1;
return 0;
}
void Graham()
{
for(int i = 2; i <= cnt; i++)
{
if(p[i].y < p[1].y)
{
double tmp;
tmp = p[1].y; p[1].y = p[i].y; p[i].y = tmp;
tmp = p[1].x; p[1].x = p[i].x; p[i].x = tmp;
}
}
sort(p+2, p+1+cnt, cmp);
tot = 1;
s[1] = p[1];
for(int i = 2; i <= cnt; i++)
{
while(tot > 1 && vp(s[tot-1], s[tot], s[tot], p[i]) <= 0)
tot--;
s[++tot] = p[i];
}
s[tot+1] = p[1];
}
int main()
{
scanf("%d", &n);
scanf("%lf%lf%lf", &a, &b, &r);
a -= 2*r, b -= 2*r; a /= 2, b /= 2;
length = sqrt(a*a+b*b); //对角线长度的一半
beta = asin(a/length);
for(int i = 1; i <= n; i++)
{
scanf("%lf%lf%lf", &x, &y, &theta);
p[++cnt] = get_point(1, theta); //右上角
p[++cnt] = get_point(2, theta); //右下角
p[++cnt] = get_point(3, theta); //左上角
p[++cnt] = get_point(4, theta); //左下角
}
Graham();
double ans = PI*2*r;
for(int i = 1; i <= tot; i++)
ans += dis(s[i], s[i+1]);
printf("%.2f\n", ans);
return 0;
}