POJ 2991 Crane (线段树 维护旋转的向量和)

题目大意:

就是多个木棍初始摆在y轴上相邻的木棍间有连接着他们的东西, 相当于关节

然后每次操作旋转某个关节, 求旋转后的端点的坐标


大致思路:

首先将所有的木棍拆成多个向量, 那么考虑到向量的和v1 + v2 + ... + vk即为顶点的坐标, 并且多个向量的和旋转得到的角, 和这所有的向量都旋转同样的角再求和得到的向量是同样的

也就是说向量的旋转的时机不影响加法运算于是可以用线段树维护一个向量的数组, 每次区间更新, 利用懒惰标记, 然后逆时针旋转的话有旋转的公式可以很快确定

对于每次询问直接求所有向量的和就可以了, 总体复杂度O(nlogn)


代码如下:

Result  :  Accepted     Memory  :  1168 KB    Time  :  844 ms

/*
 * Author: Gatevin
 * Created Time:  2015/8/17 22:49:05
 * File Name: Sakura_Chiyo.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define maxn 10010

int n, len[maxn];
const double PI = acos(-1.0);

struct Segment_Tree
{
    #define lson l, mid, rt << 1
    #define rson mid + 1, r, rt << 1 | 1
    pair<double, double> p[maxn << 2];//向量
    double theta[maxn << 2];
    void pushUp(int rt)
    {
        p[rt].first = p[rt << 1].first + p[rt << 1 | 1].first;
        p[rt].second = p[rt << 1].second + p[rt << 1 | 1].second;
        return;
    }
    void pushDown(int rt)
    {
        if(theta[rt])
        {
            theta[rt << 1] += theta[rt];
            theta[rt << 1 | 1] += theta[rt];
            p[rt << 1] = make_pair(p[rt << 1].first*cos(theta[rt]) - p[rt << 1].second*sin(theta[rt]),
                                   p[rt << 1].first*sin(theta[rt]) + p[rt << 1].second*cos(theta[rt]));
            p[rt << 1 | 1] = make_pair(p[rt << 1 | 1].first*cos(theta[rt]) - p[rt << 1 | 1].second*sin(theta[rt]),
                                       p[rt << 1 | 1].first*sin(theta[rt]) + p[rt << 1 | 1].second*cos(theta[rt]));
            theta[rt] = 0;
        }
        return;
    }
    void build(int l, int r, int rt)
    {
        theta[rt] = 0;//逆时针方向需要的旋转角
        if(l == r)
        {
            p[rt] = make_pair(0., len[l]*1.);
            return;
        }
        int mid = (l + r) >> 1;
        build(lson);
        build(rson);
        pushUp(rt);
    }
    void update(int l, int r, int rt, int L, int R, double angle)//逆时针多旋转angle
    {
        if(l >= L && r <= R)
        {
            theta[rt] += angle;
            p[rt] = make_pair(p[rt].first*cos(angle) - p[rt].second*sin(angle),
                              p[rt].first*sin(angle) + p[rt].second*cos(angle));
            return;
        }
        int mid = (l + r) >> 1;
        pushDown(rt);
        if(mid >= L) update(lson, L, R, angle);
        if(mid + 1 <= R) update(rson, L, R, angle);
        pushUp(rt);
    }
    pair<double, double> query(int l, int r, int rt, int L, int R)
    {
        if(l >= L && r <= R)
            return p[rt];
        int mid = (l + r) >> 1;
        pushDown(rt);
        pair<double, double> ret = make_pair(0., 0.);
        pair<double, double> tmp;
        if(mid >= L)
        {
            tmp = query(lson, L, R);
            ret.first += tmp.first, ret.second += tmp.second;
        }
        if(mid + 1 <= R)
        {
            tmp = query(rson, L, R);
            ret.first += tmp.first, ret.second += tmp.second;
        }
        return ret;
    }
};

Segment_Tree ST;
double angle[maxn];

int main()
{
    int n, Q;
    while(~scanf("%d %d", &n, &Q))
    {
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &len[i]);
            angle[i] = PI;
        }
        ST.build(1, n, 1);
        int p, an;
        for(int i = 1; i <= Q; i++)
        {
            scanf("%d %d", &p, &an);
            double change = an/180.*PI - angle[p];
            angle[p] = an/180.*PI;
            ST.update(1, n, 1, p + 1, n, change);
            pair<double, double> ans = ST.query(1, n, 1, 1, n);
            printf("%.2f %.2f\n", ans.first, ans.second);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值