挑战程序设计竞赛3.3例题:Crane POJ - 2991

Posted jacobfun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了挑战程序设计竞赛3.3例题:Crane POJ - 2991相关的知识,希望对你有一定的参考价值。

ACM has bought a new crane (crane -- je?áb) . The crane consists of n segments of various lengths, connected by flexible joints. The end of the i-th segment is joined to the beginning of the i + 1-th one, for 1 ≤ i < n. The beginning of the first segment is fixed at point with coordinates (0, 0) and its end at point with coordinates (0, w), where w is the length of the first segment. All of the segments lie always in one plane, and the joints allow arbitrary rotation in that plane. After series of unpleasant accidents, it was decided that software that controls the crane must contain a piece of code that constantly checks the position of the end of crane, and stops the crane if a collision should happen.

Your task is to write a part of this software that determines the position of the end of the n-th segment after each command. The state of the crane is determined by the angles between consecutive segments. Initially, all of the angles are straight, i.e., 180 o. The operator issues commands that change the angle in exactly one joint.

Input

The input consists of several instances, separated by single empty lines.

The first line of each instance consists of two integers 1 ≤ n ≤10 000 and c 0 separated by a single space -- the number of segments of the crane and the number of commands. The second line consists of n integers l1,..., ln (1 li 100) separated by single spaces. The length of the i-th segment of the crane is li. The following c lines specify the commands of the operator. Each line describing the command consists of two integers s and a (1 ≤ s < n, 0 ≤ a ≤ 359) separated by a single space -- the order to change the angle between the s-th and the s + 1-th segment to a degrees (the angle is measured counterclockwise from the s-th to the s + 1-th segment).

Output

The output for each instance consists of c lines. The i-th of the lines consists of two rational numbers x and y separated by a single space -- the coordinates of the end of the n-th segment after the i-th command, rounded to two digits after the decimal point.

The outputs for each two consecutive instances must be separated by a single empty line.

Sample Input

2 1
10 5
1 90

3 2
5 5 5
1 270
2 90

Sample Output

5.00 10.00

-10.00 5.00
-5.00 10.00
这是一道比较搞的线段树+计算几何题,结合白书简单归纳下本题:
1.这道题因为每次修改一个值以后从第n块的坐标都会发生变化,所以如果2e5的数据量去模拟当场超时,而用logn的模拟方法就是线段树。
2.所以说得用线段树,算坐标很麻烦,不如直接计算两段向量的和直接算出坐标,第一段向量A的坐标从第一块指向到n/2块,第二段向量B的坐标从n/2到n + 1(都是起始块的头指向末尾块的头,例如n = 2,第一块1头->2头,第二块2头->3头(也就是2尾))。
3.我们假定向量A始终在y轴上,我们如果知道了向量B与y轴的角度就能知道答案了,但是,这个角度因为我们假定A始终在y轴上而无法实现,但是我们可以记录B相对A的角度,通过B的长度乘以cos得到横坐标,但是就白书上给的具体公式而言,我是这么想的:首先题目以-90度为0点,所以当输入a时(a为输入的角度),角度从Π变到了a度,以y轴的负轴为0,逆时针为正,一开始B向量就是Π,后来变成了a,用a-Π就得到了变化的情况,但是我们知道,最开始B向量相对坐标轴(注意,这里不是相对y轴负轴)为90度,变化了a-Π,所以实际上相对于坐标轴变化了a-Π+Π/2度,所以应当是cos(a-Π/2),举例子:当有2个线段,把第一个与第二个的夹角变成90度,实际上相对坐标轴B向量与坐标轴角度为0(与实际的情况也相同),cos0 = 1, sin0 = 0,所以终点的坐标为(xa(A向量的横轴长度)+ |B|*cos0,ya + |B| *sin0)。
4.3这是一开始的情况,所以我们必须考虑接下来的多个的情况,如果更新只有一个线段为B的向量之后,这个A向量与B向量矢量和结果C如果作为别的的B向量,那么实际上直接拿C向量计算x,y轴相对新的A向量的增量是不会出错的,因为已经在前一次递归中算过了,所以无需再次累加新的A向量与C向量的夹角,但是,如果C向量是新的A向量,那么当我们把新的A摆到y正向时,新的B的末端与原点连成的向量与x正向所呈的角度减少了一定角度,而这个角度等于把新的A摆到y正向移动的角度,所以我们不能仅仅只算AB间夹角的影响,还要考虑A与y轴正向的夹角,因为缺少的角度是等于摆到y轴正向的角度的,所以当C作为新的A时,B的末端相对坐标轴不只是之前的向量相对坐标轴的角度,还要加上A的偏移角度,这样才能得到正确的结果。
//看此代码之前一定要在看看白书的3.3.1的内容和例题
#include <cstdio>
#include <cmath>

int n, c;
int  l[10005], s, ang;//ang对应白书的A数组
double a;//对应白书solve()的double a;
struct Node{
    double x;
    double y;
    double a;//该向量对应与坐标轴的角度,对应白书的ang
}node[(1<<15) - 1];
double pre[10005];//对应白书的prv【】
void init(int k, int start, int end)
{
    node[k].x = node[k].a = 0.0;
    if(end - start == 1)
        node[k].y = l[start];
    else
    {
        int chl = k * 2 + 1, chr = k * 2 + 2;
        int mid = (start + end) / 2;
        init(chl, start, mid);
        init(chr, mid, end);
        node[k].y = node[chl].y + node[chr].y;
    }
}
void update(int k, int start, int end)
{
    if(start >= s || end <= s)//如果要更新的点不在现在的范围内(一定要包含,且不为两端点),那么不会该值不会发生改变,所以没必要。
        return;
    else
    {
        int chl = 2 * k + 1, chr = 2 * k + 2;
        int mid = (start + end) / 2;
        update(chl, start, mid);
        update(chr, mid, end);
        if(s <= mid)
//如果修改的s在此段作为B向量,那么上次递归时B向量已经更新了,但如果作为A向量,那么B末端相对原点角度会移动一个角度,这个角度等于A变化的角度,所以我们假定A旋转到y正向,因为我们只保存B相对A的角度,那么B此时相对坐标轴是原来B相对坐标轴的角度减去A变化的角度,所以得加起来 node[k].a += a - pre[s]; //新的x = A的x + |B| * cos(A变化的角度 + B之前的角度);变化的角度对应node[k].a,之前的对应arccos(node[chr].x / |B|) double sing = sin(node[k].a), cosg = cos(node[k].a); node[k].x = node[chl].x + (cosg * node[chr].x - sing * node[chr].y); node[k].y = node[chl].y + (sing * node[chr].x + cosg * node[chr].y); } } int main() { while(scanf("%d %d", &n, &c) != EOF) { for(int i = 0; i < n; i++) { scanf("%d", &l[i]); pre[i] = acos(-1); } init(0, 0, n); for(int i = 0; i < c; i++) { scanf("%d %d", &s, &ang); a = ang / 360.0 * 2 * acos(-1); update(0, 0, n); pre[s] = a; printf("%.2f %.2f ", node[0].x, node[0].y); } } return 0; }

 



以上是关于挑战程序设计竞赛3.3例题:Crane POJ - 2991的主要内容,如果未能解决你的问题,请参考以下文章

Poj2919 Crane

poj1182食物链_并查集_挑战程序设计竞赛例题

挑战程序设计竞赛 3.3 活用各种数据结构

挑战程序设计竞赛2.2习题:Allowance POJ - 3040

挑战程序设计竞赛2.3:Wooden Sticks POJ - 1065

挑战程序设计竞赛2.3习题:Cheapest Palindrome POJ - 3280