POJ - 2991 Crane (线段树 线段角度改变)

题目链接

有N条线段,初始状态全部竖直向上,在每条线段的最上方有一个可以旋转的关节点。

当一个关节点旋转时,它上面所有的线段都会跟着旋转。

每次旋转后,求最上面一个关节点的坐标

 

区间修改。每个节点 存一个向量,从起始点指向结束点,还要存一个要转到既定位置,要转多少角度。

题目给的是角度,我们要换成 rad 形式。

题目给的角度并不是我们在线段树种存的角度。

 

 

求一个向量(x0,y0)逆时针旋转B度后的向量有一个公式:
x1= x0 * cosB - y0 * sinB
y1 = x0 * sinB + y0 * cosB
顺时针就把-B代入:
x1= x0 * cosB + y0 * sinB
y1 = -x0 * sinB + y0 * cosB
 

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
const int N = 2e4 + 10;
const double PI = acos(-1.0);
int n,m,x,t,y,pre[N],z;
int  yy[N],an;
struct node {
    int a,b,l,r,ang;
    double vx,vy;
    int cal_mid(){
        return (a + b) / 2;
    }
}f[N*2];
double get(int x){
    return x * PI / 180; //转化成弧度制。
}
void Rotate(int p, int d){
    double rad = get(d);
    double  xx = f[p].vx, yy = f[p].vy;
    f[p].vx = xx * cos(rad) - yy * sin(rad); //套用公式。
    f[p].vy = xx * sin(rad) + yy * cos(rad);
    return;
}
void push_down(int p){
    if (f[p].ang == 0) return;
    f[f[p].l].ang += f[p].ang;
    f[f[p].r].ang += f[p].ang;
    Rotate(f[p].l,f[p].ang);
    Rotate(f[p].r,f[p].ang);
    f[p].ang = 0;
    return;
}
void build(int p, int a, int b){
    f[p].a = a; f[p].b = b;
    f[p].vx =f[p].ang = 0;
    if (a + 1 == b){
        f[p].vy = yy[a];
        return;
    }
    int m = f[p].cal_mid();
    t++; f[p].l = t; build(t,a,m);
    t++; f[p].r = t; build(t,m,b);
    f[p].vy = f[f[p].l].vy + f[f[p].r].vy;
    return;
}
void push_up(int p){
    f[p].vx = f[f[p].l].vx + f[f[p].r].vx;
    f[p].vy = f[f[p].l].vy + f[f[p].r].vy;
    return;
}
void Insert(int p){
    if (x <= f[p].a && y >= f[p].b - 1){
        f[p].ang += z;
        Rotate(p,z);
        return;
    }
    push_down(p);
    int m = f[p].cal_mid();
    if (x < m) Insert(f[p].l);
    if (y >= m) Insert(f[p].r);
    push_up(p);
    return;
}
int main() {
    int num = 0;
    while(scanf("%d%d",&n,&m) == 2){
        if (num != 0) printf("\n");
        num++;
        for (int i = 1; i <= n; i++)
            pre[i] = 180; //当前线段和前一个线段之间的角度
        for (int i = 1; i <= n; i++)
            scanf("%d",&yy[i]);
        t = 1;
        build(1,1,n+1);
        for (int i = 1; i <= m; i++){
            scanf("%d%d",&x,&an);
            x++;  //我们要改的是 读入的x 下一条线段。
            y = n;
            z = an - pre[x]; // 要转多少角度。
            pre[x] = an;
            Insert(1);
            printf("%.2lf %.2lf\n",f[1].vx,f[1].vy);
        }
    }
    return 0;
}

/*

10 10
2 8 5 1 10 5 9 9 3 5
3 305
7 241
6 241
9 115
1 147
7 351
1 302
8 292
6 141
6 278

-34.40 -9.09
-18.70 -16.25
3.48 -9.77
-0.95 -12.81
-8.86 -9.91
-1.03 13.08
-3.74 -8.48
4.05 -16.70
-1.83 -10.51
7.32 -15.38
 */

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值