码迷,mamicode.com
首页 > 编程语言 > 详细

转载::POJ 2991 线段树+计算几何(有c++结构体操作)

时间:2015-05-16 20:19:47      阅读:260      评论:0      收藏:0      [点我收藏+]

标签:

POJ 2991 线段树+计算几何

(2011-02-27 21:13:44)
标签:

杂谈

分类: OI

话说这一题真的是很恶心很恶心,不过确实改变了我对线段树的一些看法,算是很经典的题目。

题意:有一个吊车由很多个不同长度的线段组成,一开始是一条长直线起点在(0,0),尾节点在(0,sum[n]),每条线段之间的夹角的初始值是180度。然后有一些操作a、 b将第a条线段和a+1之间的夹角变成b度,经过每一次操作都要求出尾节点的坐标。

 

  首先要用到一个计算几何的知识(没学过。。请教而来):

  对于一个向量x,y,逆时针旋转a度得到的新向量x‘、y‘,可以由如下推导

  [x y]*[cos(a)  sin(a)] = [x‘ y‘]

        [-sin(a) cos(a)]

 

    然后再是一个很简单的结论,一个线段旋转后,后面的线段相对之间的角度没有发生变化,从这一点我觉得很容易的就可以和线段树的标记下传结合起来。

    线段树记录线段的起始和结束节点,以及标记下传的改变角度。

    对于一条线段的修改是这样操作的:

    如果该线段的起点正好等于一线段树中一个节点的起点:将该向量以首节点直接旋转,并将这个角度计入标记下传中的角度,标记这条线段。

    否则将这条线段存在于左子树的部分旋转,然后修改右子树的两端点然后继续旋转右子树。

    最后将目前节点右子树的末坐标上传。

 

    我觉得本题的精华之处就在于左子树和右子树两者信息的联合,左子树修改后影响了右子树,这样的题目还是第一次遇到。

    代码写的不好,借鉴了别人的一些。

   

#include <cstdio>
#include <cmath>
#define PI acos(-1.0)
#define eps 1e-8
#define L(x) (x << 1)
#define R(x) ((x << 1) | 1)
#define maxn 10001

int N, Q;
int len[maxn], sum[maxn];
double mySin[720], myCos[720];

struct Point
{
    double x, y;

    Point operator +(const Point & a)const
    {
        Point ret;
        ret.x = x + a.x;
        ret.y = y + a.y;
        return ret;
    }

    Point operator -(const Point & a)const
    {
        Point ret;
        ret.x = x - a.x;
        ret.y = y - a.y;
        return ret;
    }
};

struct Segtree
{
    int l, r, deg;
    Point s, e;
    bool mark;

    int mid()
    {
        return (l + r) >> 1;
    }

    void Rotate(int angle)
    {
        double dx = e.x - s.x, dy = e.y - s.y;
        double newx = dx * myCos[angle + 360] - dy * mySin[angle + 360];
        double newy = dx * mySin[angle + 360] + dy * myCos[angle + 360];
        e.x = s.x + newx;
        e.y = s.y + newy;
    }
} tree[maxn * 4];

void Build(int rt, int l, int r)
{
    tree[rt].l = l, tree[rt].r = r;
    tree[rt].deg = 0;
    tree[rt].mark = 0;
    tree[rt].s.x = tree[rt].e.x = 0;
    tree[rt].s.y = sum[l] - len[l];
    tree[rt].e.y = sum[r];
    if (l != r)
    {
        Build(L(rt), l, tree[rt].mid());
        Build(R(rt), tree[rt].mid() + 1, r);
    }
}

void Update(int rt)
{
    int l = L(rt), r = R(rt);
    //提出向量 根据新节点进行旋转
    tree[l].deg += tree[rt].deg;
    tree[l].deg %= 360;
    tree[l].e = tree[l].e - tree[l].s + tree[rt].s;
    tree[l].s = tree[rt].s;
    tree[l].Rotate(tree[rt].deg);

    tree[r].e = tree[r].e - tree[r].s + tree[l].e;
    tree[r].s = tree[l].e;
    tree[r].deg += tree[rt].deg;
    tree[r].deg %= 360;

    tree[r].Rotate(tree[rt].deg);

    tree[l].mark = tree[r].mark = 1;
    tree[rt].mark = 0;
    tree[rt].deg = 0;
}

int Query(int rt, int order)
{
    if (tree[rt].l == tree[rt].r) return tree[rt].deg;
    //只用叶节点记录真实的角度
    if (tree[rt].mark) Update(rt);
    int LL = L(rt), RR = R(rt);
    if (order <= tree[LL].r) return Query(LL, order);
    else return Query(RR, order);
}

void Change(int rt, int l, int angle)
{
    if (tree[rt].l != tree[rt].r && tree[rt].mark) Update(rt);
    if (tree[rt].l == l)
    {
        tree[rt].deg += angle;
        tree[rt].deg %= 360;
        tree[rt].Rotate(angle);
        tree[rt].mark = 1;
        return;
    }
    int LL = L(rt), RR = R(rt);
    int mid = (tree[rt].l + tree[rt].r) >> 1;
    if (l <= mid)
    {
        Change(LL, l, angle);
        tree[RR].e = tree[RR].e + tree[LL].e - tree[RR].s;
        tree[RR].s = tree[LL].e;
        Change(RR, mid + 1, angle);
    }
    else Change(RR, l, angle);
    tree[rt].e = tree[RR].e;
}

int main()
{
    int cases = 0;
    for (int i = -360; i <= 360; ++i)
    {
        mySin[i + 360] = sin(i * PI / 180);
        myCos[i + 360] = cos(i * PI / 180);
    }
    while (~scanf("%d%d", &N, &Q))
    {
        if (cases++) puts("");
        sum[0] = 0;
        for (int i = 1; i <= N; ++i)
        {
            scanf("%d", len + i);
            sum[i] = sum[i - 1] + len[i];
        }
        Build(1, 1, N);

 

       for (int i = 1; i <= Q; ++i)
        {
            int a, b;
            scanf("%d%d", &a, &b);
            b = ((Query(1, a) - Query(1, a + 1) + 180 + b) % 360 + 360) % 360;
            Change(1, a + 1, b);
            printf("%.2lf %.2lf\n", tree[1].e.x + eps, tree[1].e.y + eps);
        }
    }
    return 0;
}

转载::POJ 2991 线段树+计算几何(有c++结构体操作)

标签:

原文地址:http://www.cnblogs.com/linkzijun/p/4508294.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!