HDU 2991 Crane

本文详细介绍了如何解决吊车手臂旋转问题的算法优化过程,通过将问题转化为向量操作,利用线段树实现高效更新,最终得出每次操作后第n个手臂的末节点坐标。文章深入探讨了算法设计与复杂度优化,提供了宝贵的编程实践经验和思考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

有一个吊车有很多段手臂,这些手臂都是相连着的,最开始每个手臂都垂直于x轴。

给出每个手臂的长度,和每次操作的手臂编号(从1 开始)以及每次操作把两个手臂在逆时针方向的夹角修改为多少度。

数据:

输入:

一个n 和一个c

n个 手臂分别的长度

c行 操作手臂编号 修改角度

输出:

每次操作后第n个手臂的末节点。

思路:

(一道题做三天也真是醉了。。。。,

最开始的思路是一个点相对另一个点旋转,求出每次的坐标,但是后来发现这么做无法和线段树的成段更新结合起来,因为每次操作都是是需要上次更新的节点信息,这样还需要先把上次的坐标算出来,所以其实复杂度就变成了O(c * k)

后来百度到的思路是这样的:

把所有的手臂都当成一个向量来操作,因为向量是绝对的,把相对坐标旋转的不确定性给避开了。

有两个好处。

1.尾节点的坐标就可以看成从第一个向量到最后一个向量的向量和。

2.进行旋转的时候,只需要旋转从修改节点下一个到最后节点的向量和就可以了。因为这个节点就代表了下面所有的向量的向量和,而每个向量旋转的角度都是相同的,向量和旋转的角度也是相同的, 并且,旋转的角度是可以累加计算的,从而可以和线段树成段更新的高效性结合在一起。


最后,送给自己一句话:省去现在必须付出的时间是愚蠢的行为,因为过后你需要用万倍的时间去弥补。

(就是因为不好好读题,一道题陷入误区1天,因为题意理解错了,又耽误两天)

Code:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<string>
#include<queue>
#include<stack>
#include<bitset>
#include<set>
#include<map>
#include<cctype>
#include<vector>

#define TEST

#define LL long long
#define Mt(f, x) memset(f, x, sizeof(f));
#define rep(i, s, e) for(int i = (s); i <= (e); ++i)
#ifdef TEST
    #define See(a) cout << #a << " = " << a << endl;
    #define See2(a, b) cout << #a << " = " << a << ' ' << #b << " = " << b << endl;
    #define debug(a, s, e){ rep(_i, s, e) {cout << a[_i] << ' '; }cout << endl;}
    #define debug2(a, s, e, ss, ee) rep(i_, s, e) {debug(a[i_], ss, ee);}
#else
    #define See(a)
    #define See2(a, b)
    #define debug(a, s, e)
    #define debug2(a, s, e, ss, ee)
#endif

const int MAX = 2e9;
const int MIN = -2e9;
const double PI = acos(-1.0);
const double eps = 1e-8;

using namespace std;

#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1

const int N = 10000 + 5;

double dx[N << 2], dy[N << 2];
int ang[N << 2], bef[N]; //记录每个向量旋转的角度,和每次手臂之间的夹角以计算每次的差量。

void rot(int rt, int rad) //计算旋转和的坐标
{
    double d = rad / 180.0 * PI;
    double x = dx[rt], y = dy[rt];
    dx[rt] = x * cos(d) - y * sin(d);
    dy[rt] = x * sin(d) + y * cos(d);
}

void pushUp(int rt)
{
    dx[rt] = dx[rt << 1] + dx[rt << 1 | 1];
    dy[rt] = dy[rt << 1] + dy[rt << 1 | 1];
}

void pushDown(int rt)
{
    if(ang[rt])
    {
        rot(rt << 1, ang[rt]);
        rot(rt << 1 | 1, ang[rt]);
        ang[rt << 1] += ang[rt];
        ang[rt << 1 | 1] += ang[rt];
        ang[rt] = 0;
    }
}

void build(int l, int r, int rt)
{
    if(l == r)
    {
        dx[rt] = 0;
        scanf("%lf", &dy[rt]);
        return ;
    }
    int m = (l + r) >> 1;
    build(lson);
    build(rson);
    pushUp(rt);
}

void update(int L, int R, int rad, int l, int r, int rt)
{
    if(l >= L && r <= R)
    {
        rot(rt, rad);
        ang[rt] += rad;
        return ;
    }
    pushDown(rt);
    int m = (l + r) >> 1;
    if(L <= m)
    {
        update(L, R, rad, lson);
    }
    if(R > m)
    {
        update(L, R, rad, rson);
    }
    pushUp(rt);
}

int main()
{
    int n, c;
    bool first = true;
    while(~scanf("%d%d", &n, &c))
    {
        printf(first ? "" : "\n");
        first = false;
        Mt(bef, 0);
        Mt(ang, 0);
        build(1, n, 1);
        while(c--)
        {
            int b, d;
            scanf("%d%d", &b, &d);
            int rad = d - 180 - bef[b];//计算与上次角度的差量
            bef[b] = d - 180; //更新角度
            update(b + 1, n, rad, 1, n, 1);//更新从b + 1,到n 段的角度与坐标)
            printf("%.2f %.2f\n", dx[1], dy[1]);
        }
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值