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

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;
}

时间: 2024-10-10 02:11:01

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

poj 2991 线段树

1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <stack> 5 #include <queue> 6 #include <map> 7 #include <algorithm> 8 #include <vector> 9 #include <cmath> 10 #define pi 3.14159

POJ - 2991 Crane (线段树+计算几何)

Description 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 t

POJ 2528 (线段树+离散化) Mayor&#39;s posters

因为将每个单位都作为一个最小单元的话会爆内存的 所以,将海报的每个端点进行排序,将这些端点最为最小的区间. 毕竟是刚刚接触线段树,理解起来还有些吃力,还是那句话,题做多了慢慢就好了. 萌萌的AC代码君贴上. 1 //#define LOCAL 2 #include <iostream> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 7 int n; 8 struct CPost 9

poj 2777 线段树的区间更新

Count Color Time Limit: 1000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Java class name: Main [Submit] [Status] [Discuss] Description Chosen Problem Solving and Program design as an optional course, you are required to solve al

poj 2750(线段树的动态规划)

Potted Flower Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4186   Accepted: 1581 Description The little cat takes over the management of a new park. There is a large circular statue in the center of the park, surrounded by N pots of f

POJ 2570 线段树

Potted Flower Time Limit: 2000 MS Memory Limit: 65536 KB 64-bit integer IO format: %I64d , %I64u Java class name: Main [Submit] [Status] [Discuss] Description The little cat takes over the management of a new park. There is a large circular statue in

poj 2828 线段树

http://poj.org/problem?id=2828 学到的思维: 1.变化的或者后来的优先影响前面的,那么从最后一个往前看,最后一个就成了 确定的, 并且后来的也可以确定----如果从前往后,所有的随时都不是确定的 2.线段树叶子节点直接维护区间(线段)信息,非叶子节点v维护的是以v为树根的整个子树的信息,那么假设父节点rt信息为[l,r]那么左子树维护[l,mid],右子树维护[mid+1,r]的信息.如果如果是前缀和,rt里是1-n的和,左子树1~n/2的和,右子树是n/2+1~n

POJ 3468 线段树+lazy标记

lazy标记 Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%I64d & %I64u Submit Status Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to

POJ 3667 线段树的区间合并简单问题

题目大意:有一排标号1-N的房间.操作一:询问是不是有连续长度为a的空房间,有的话住进最左边(占用a个房间)操作二:将[a,a+b-1]的房间清空(腾出b个房间)思路:记录每个区间中“靠左”“靠右”“中间”的空房间线段树操作:update:区间替换query:询问满足条件的最左端点 题目链接: http://vjudge.net/problem/viewProblem.action?id=10354 题目操作: 因为这里找从最左边住起的房间,所以这里不能像常规地写query函数那样写了,终于发现