POJ2991-Crane

维护一个线段树和一个相对变化角度的数组,每个线段树节点表示从左子树到右子树的向量,每条线段(叶子节点)一经初始化后就不改变vx, vy,只需更新父节点即可。

#include <cstdio>
#include <cmath>

const double pi = acos(-1.0);
const int st_size = (1 << 15) - 1;
const int maxn = 10000 + 10;
const int maxc = 10000 + 10;

int L[maxn];
int S[maxc], A[maxn];

//线段树所维护的数据
double vx[st_size], vy[st_size];    //各节点的向量
double ang[st_size];                //各节点的角度

//为了查询角度的变化而保存的当前角度的数组
double prv[maxn];

//初始化线段树
//k是节点的编号,l, r表示当前节点对应的是[l, r)区间
void init(int k, int l, int r) {
    ang[k] = vx[k] = 0.0;

    if (r - l == 1) {
        //叶子节点
        vy[k] = L[l];
    } else {
        //非叶子节点
        int chl = k * 2 + 1;
        int chr = k * 2 + 2;
        init(chl, l, (l + r) / 2);
        init(chr, (l + r) / 2, r);
        //初始时线段按竖直排列,vx为0,只将vy相加即可
        vy[k] = vy[chl] + vy[chr];
    }
}

//把s和s+1的角度变为a
//v是节点的编号,l, r表示当前节点对应的是[l, r)区间
void change(int s, double a, int v, int l, int r) {
    if (s <= l) {
        return;
    } else if (s < r) {
        int chl = v * 2 + 1;
        int chr = v * 2 + 2;
        int m = (l + r) / 2;
        change(s, a, chl, l, m);
        change(s, a, chr, m, r);
        if (s <= m) {
            ang[v] += a;
        }

        double s = sin(ang[v]), c = cos(ang[v]);
        vx[v] = vx[chl] + (c * vx[chr] - s * vy[chr]);
        vy[v] = vy[chl] + (s * vx[chr] + c * vy[chr]);
    }
}


int main(int argc, char const *argv[]) {
    int n, c;
    while (scanf("%d%d", &n, &c) == 2) {
        for (int i = 0; i < n; i++) {
            scanf("%d", &L[i]);
        }
        for (int i = 0; i < c; i++) {
            scanf("%d%d", &S[i], &A[i]);
        }

        //初始化
        init(0, 0, n);
        for (int i = 1; i < n; i++) {
            prv[i] = pi;
        }

        //处理操作
        for (int i = 0; i < c; i++) {
            int s = S[i];
            double a = A[i] / 360.0 * 2 * pi;

            change(s, a - prv[s], 0, 0, n);
            prv[s] = a;

            printf("%.2f %.2f\n", vx[0], vy[0]);
        }
        putchar('\n');
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值