POJ 2991 Crane 线段树

news2024/11/22 19:40:50

一、题目大意

我们有一台起重机的机械臂,它由多个节相连组成,如下所示。

起初的时候,所有的节之间的角度都是180度,是竖直的,我们可以扭转其中任意两个节的角度,每一次移动后题目需要输出题目最后一个点相对于第一个点偏移的x和y如下两张图所示。

其中 \sin(\frac{\pi}{6})\times20\times (-1)=-10,然后 \cos\frac{\pi}{6}\times20+5=22.32

从图中其实可以推断一个猜想,我们对1和2之间角度偏移了30°,那么2后面的3,4,5其实都会偏移2,即对于[1,n]个节,偏移 i-1 到i 的角度,那么[i,n]其实都要偏移同样的角度。

二、解题思路

其实不难看出了,这是一个区间操作的问题,那么考虑下,假如说我有10000节,我把第一节和第二节之间的角度偏移到210,动了30度,那么除了第二节,后面的9997节其实也会都再偏移30度,假设我们从0开始计下标,那么除了下标为0的元素不用改,其他[1,10000)的下标的元素都得变!如果一个一个更新,那么复杂性太高了,如果操作10000次,那么就是10000*10000*10000次的数组更新,分钟甚至小时的时间蜗牛速度!所以就考虑下区间操作,是不是可以不更新所有的点!

我们可以构建一个线段树,每个节点保存3个值,如下

1、节点相对它起点的偏移的x

2、节点相对于它终点偏移的y

3、节点的子节点共同偏移的角度angle

这样的话我们要更新第[2,10000]的元素也就是[0,10000)下标的数组的值的话,如果使用线段树会快很多,10000的线段树有10多层,我就画一个简要的图,例如下图,我们更新画红圈圈的两个点就可以代表对[2048,8192)这6000多个数组元素进行赋值,那么有了它,其实本题目就可以在O(n*n*logn)的复杂性完成。

那么如何实现区间修改角度呢,拿一个之后4个元素的起重机,假设它们的长度都是5,那么每个节点需要记录的值有几部分。

1、它管理的区间[l,r)

2、它相对于它起点x偏移和y偏移(x,y)

3、它的所有子节点一起动过的角度 dAngle

假如说我们要对1到2节点的角度进行改变,从180到210变化30,那么对于线段树的变化如下。

这个更新的步骤如下,假设旋转的角度为 i-1到 i 转 Ω度,

1、如果节点的 [l,r) 中 l > i 或者 r <= i,直接跳过

2、如果节点的 [l,r)中 l>=i,那么更新这个节点的x和y为转动Ω度后的x和y,同时记录之前的x和y为x_和y_,然后这个节点dAngle的度数增加30,代表它的子节点都偏移30度,然后直接向上去更新父节点,更新父节点时,先用它原来的x_和y_旋转父节点的dAngle度,之后用父节点的值减去求出的值,然后用子节点新的值x和y也去旋转父节点的dAngle度,之后父节点的值加上这个值。

然后递归的更新父节点的父节点,每次更新父节点时,用它直接相连的子节点变化之前的值,偏移父节点的dAngle度,然后父节点的值减去这个值,之后计算直接相连的子节点的值变化之后的值,偏移父节点的dAngle度,然后父节点的值加上这个值。

一直利用上一段的操作,更新到根节点即可。

3、如果节点的 l < r,那么代表这个节点中的子节点会完全包含,则递归它的两个子节点。

之后我们要输出答案的时候,只需要输出根节点的x和y的偏移量即可。

那么还有有点就是如何去计算某个x和偏移了Ω角度后的值,其实不难,例如将x1和y1原来的角度时Ω1度,旋转Ω2度到x2和y2,如下所示。

那么我们不难看出这像是两个斜边相同的直角三角形,然后我们还知道Ω1、Ω2、x1和x2的值,那么我们根据斜边相同的等值关系去列方程

x1/sin(Ω1) =   x2/sin(Ω1+Ω2)

y1/cos(Ω1) =   y2/cos(Ω1+Ω2)

解出这个二元一次方程,求出x2和y2即可,化简下。

\frac{x1}{\sin{ \Omega1 }} = \frac{x2}{\sin{(\Omega1+\Omega2)}}

x2=\frac{x1\times(\sin{ \Omega1}\cos\Omega2+\cos{ \Omega1}\sin\Omega2)}{\sin{ \Omega1 }}

x2=x1\times\cos\Omega2+x1\times\frac{cos\Omega1}{sin\Omega1}\times\sin{ \Omega1 }

x2=x1\times\cos\Omega2+x1\times\frac{1}{tan\Omega1}\times\sin{ \Omega1 }

\frac{y1}{\cos{ \Omega1 }} = \frac{y2}{\cos{(\Omega1+\Omega2)}}

y2=\frac{y1\times(\cos{ \Omega1}\cos\Omega2-\sin{ \Omega1}\sin\Omega2)}{\cos{ \Omega1 }}

y2=x1\times\cos\Omega2+x1\times\tan\Omega1\times\sin{ \Omega1 }

然后我们直到tan(Ω1)=x/(-y),那么其实也就可以看出,求出x2和y2不需要知道x1和y1之前偏移的角度,只需要直到在某次操作时,变化的角度即可。

然后需要注意的是,计算三角函数,需要特殊处理下90°的倍数,

1、当变化90°时,其实就是 x2=y1*(-1),y2=x1

2、变化180°时,x2=x1*(-1),y2=y1*(-1)

3、变化270°时,就是x2=y1,y2=(x1)*(-1)

然后变化的角度小于0就加上360即可,大于0就对360取余(多转一圈少转一圈x和y没有变化)

然后计算tan时,需要考虑下之前x1和y1是不是有一方为0。如果都是0,那么不用计算了,怎么转都是0,

如果x1==0,y1!=0,那么转之后 x2=y1 * sin(Ω2) * (-1),y2=y1 * cos(Ω2) 

如果x1!=0,y1==0,那么转之后 x2=x1 * cos(Ω2) * (-1),y2=x1 * sin(Ω2) 

然后注意下,我以上说的Ω1是之前的角度,Ω2是之后的角度-之前的角度的差,就是我们需要根据输入来计算的,不是直接用的输入的那个角度。

三、代码

#include <iostream>
#include <cmath>
using namespace std;
// 节点i相对于它的起点的x,y偏移就等于datX[i],datY[i]再旋转上它所有父节点的datAngle的和
double datX[32780], datY[32780], num[10007], tmpX, tmpY, pai = 3.141592653589793;
int n_, n, angle[10007], op, datAngle[32780];
void input()
{
    for (int i = 0; i < n_; i++)
    {
        scanf("%lf", &num[i]);
        angle[i + 1] = 180;
    }
}
void init()
{
    n = 1;
    while (n < n_)
    {
        n = n * 2;
    }
    for (int i = ((2 * n) - 2); i >= 0; i--)
    {
        if (i >= (n - 1))
        {
            datX[i] = 0.0;
            datAngle[i] = 0;
            if ((i - (n - 1)) < n_)
            {
                datY[i] = num[i - (n - 1)];
            }
            else
            {
                datY[i] = 0.0;
            }
        }
        else
        {
            datAngle[i] = 0;
            datX[i] = datX[(i * 2) + 1] + datX[(i * 2) + 2];
            datY[i] = datY[(i * 2) + 1] + datY[(i * 2) + 2];
        }
    }
}
void calcVal(int dAngle, double currentX, double currentY)
{
    if (dAngle == 0)
    {
        tmpX = currentX;
        tmpY = currentY;
    }
    else if (currentX == 0.0 && currentY == 0.0)
    {
        tmpX = currentX;
        tmpY = currentY;
    }
    else if (dAngle == 90)
    {
        tmpX = currentY * (-1.0);
        tmpY = currentX;
    }
    else if (dAngle == 180)
    {
        tmpX = currentX * (-1.0);
        tmpY = currentY * (-1.0);
    }
    else if (dAngle == 270)
    {
        tmpX = currentY;
        tmpY = currentX * (-1.0);
    }
    else if (currentX == 0.0)
    {
        double omiga = ((dAngle * (1.0)) / (180.0)) * pai;
        tmpX = sin(omiga) * currentY * (-1.0);
        tmpY = cos(omiga) * currentY;
    }
    else if (currentY == 0.0)
    {
        double omiga = ((dAngle * (1.0)) / (180.0)) * pai;
        tmpX = cos(omiga) * currentX;
        tmpY = sin(omiga) * currentX;
    }
    else
    {
        double omiga = ((dAngle * (1.0)) / (180.0)) * pai;
        tmpX = cos(omiga) * currentX + (sin(omiga) * (currentY / currentX) * (-1.0) * currentX);
        tmpY = cos(omiga) * currentY - (sin(omiga) * (currentX / currentY) * (-1.0) * currentY);
    }
}
//[l,n)
void handle(int idx, int dAngle, int i, int l, int r)
{
    if (idx <= l)
    {
        double beforeChangeX = datX[i];
        double beforeChangeY = datY[i];
        calcVal(dAngle, beforeChangeX, beforeChangeY);
        double afterChangeX = tmpX;
        double afterChangeY = tmpY;
        datX[i] = afterChangeX;
        datY[i] = afterChangeY;
        datAngle[i] = (datAngle[i] + dAngle) % 360;
        int j = i;
        while (j > 0)
        {
            j = (j - 1) / 2;
            calcVal(datAngle[j], beforeChangeX, beforeChangeY);
            beforeChangeX = datX[j];
            beforeChangeY = datY[j];
            datX[j] = datX[j] - tmpX;
            datY[j] = datY[j] - tmpY;
            calcVal(datAngle[j], afterChangeX, afterChangeY);
            datX[j] = datX[j] + tmpX;
            datY[j] = datY[j] + tmpY;
            afterChangeX = datX[j];
            afterChangeY = datY[j];
        }
    }
    else if (l < idx && r > idx)
    {
        handle(idx, dAngle, i * 2 + 1, l, (l + r) / 2);
        handle(idx, dAngle, i * 2 + 2, (l + r) / 2, r);
    }
}
int main()
{
    int nAngle, idx, cnt = 0;
    while (~scanf("%d%d", &n_, &op))
    {
        if (cnt > 0)
        {
            printf("\n");
        }
        input();
        init();
        for (int i = 0; i < op; i++)
        {
            scanf("%d%d", &idx, &nAngle);
            if (nAngle != angle[idx])
            {
                int dAngle = nAngle - angle[idx];
                if (dAngle < 0)
                {
                    dAngle = dAngle + 360;
                }
                handle(idx, dAngle, 0, 0, n);
                angle[idx] = nAngle;
            }
            printf("%.2f %.2f\n", datX[0], datY[0]);
        }
        cnt++;
    }
    return 0;
}

四、运行情况

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1044629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货 | 工商业用户负荷分析与预测系统项目

以下内容整理自2023年夏季学期大数据能力提升项目《大数据实践课》同学们所做的期末答辩汇报。 我们将从六个方面进行展示。 第一部分是项目背景与需求分析。在“双碳”目标的大背景下&#xff0c;能源电力行业面临着深刻的变革&#xff0c;负荷预测作用也更加突出。虚拟电厂由…

蓝牙无线IP网络多功能多媒体音柱带遥控

SV-29810T-蓝牙无线IP网络多功能多媒体音柱带遥控 蓝牙无线IP网络多功能多媒体音柱SV-29810T产品用途&#xff1a; ◆室外室内豪华型防水音柱式一体化网络音频解码扬声器&#xff0c;用于广播分区音频解码、声音还原作用◆应用场地如火车站、地铁、教堂、工厂、仓库、公园停车…

lv5 嵌入式开发-7 有名管道和无名管道

目录 1 进程间通信介绍 2 无名管道 2.1 无名管道特点 ​编辑 2.2 读无名管道 2.3 写无名管道 3 有名管道 3.1 有名管道特点 3.2 写有名管道 3.3 读有名管道 掌握&#xff1a;进程间通信方式介绍、无名管道特点、无名管道创建、无名管道读写特性&#xff1b;有名管道…

【面试题】——JavaIO篇(23题)

文章目录 1.什么是Java IO&#xff1f;2.如何从数据传输方式理解IO流&#xff1f;3.Java IO设计上使用了什么设计模式&#xff1f;4.什么是Java NIO&#xff1f;5.什么时BIO?6.什么是AIO?7.你怎么理解同步IO和异步IO?8.你怎么理解阻塞IO和非阻塞IO?9.IO中的输入流和输出流有…

Python项目实战:多线程并行计算 + 多进程并行计算

文章目录 一、简介&#xff1a;【进程 多进程】 【线程 多线程】1.1、系统支持的CPU核心处理器1.2、核心处理器的参数解析&#xff1a;12th Gen Intel( R ) Core( TM ) i7-12700 2.10 GHz 二、函数详解2.0、计算CPU核心数&#xff1a;os.cpu_count() mp.cpu_count()2.1、用…

MongoDB 解析:灵活文档数据库与 Docker Compose 部署

MongoDB 是一款开源、高性能的 NoSQL 数据库&#xff0c;以其无模式的文档存储格式&#xff08;BSON&#xff09;而著称&#xff0c;广泛应用于众多开源项目&#xff0c;包括但不限于 Yapi 等。它在大规模数据存储和实时数据处理方面表现出色&#xff0c;因此备受青睐。在本文中…

C++学习——优先级队列模拟实现与仿函数初步认识

目录 ​编辑 一&#xff0c;优先级队列 二&#xff0c;实现 1.构造priority_queue类 2.简单的top()与size()还有empty()函数 3.push函数 4.pop函数 5.构造函数 6.测试 三&#xff0c;仿函数 1.介绍 2.使用 一&#xff0c;优先级队列 优先级队列——priority_queue。这…

【C++】gnustl_static 与 c++_shared 的区别

参考&#xff1a;GNU与cSTL的区别与联系-爱代码爱编程​ gnustl_static 与 c_shared 的区别&#xff1a; 不同版本的 STL TSL是一个与STL兼容的多线程支持库。 STLport是一个可移植、高度兼容的STL实现。 SGI STL是最早的STL实现之一&#xff0c;对STL的发展起到了重要的作用…

深入MySQL数据库进阶实战:性能优化、高可用性与安全性

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 MySQL是世界上最流行的开…

Windows 下 MySQL 8.1.0 安装及配置图文指南,快速搭建实验学习环境

目录 下载 MySQL安装 MySQL配置 MySQL修改密码配置环境变量 卸载 MySQL开源项目微服务商城项目前后端分离项目 下载 MySQL 访问 MySQL 下载地址&#xff1a;https://dev.mysql.com/downloads/mysql/ 下载 MySQL 时&#xff0c;你可以选择 ZIP 包或 MSI 安装&#xff1a; ZIP包…

赴日IT课程分享 如何尽快就职日本IT公司?

想要做赴日IT工作&#xff0c;我们先要搞清楚一个问题&#xff0c;那就是日本IT行业的缺口真的很大吗&#xff1f;答案是肯定的&#xff0c;对于有3-5年实际开发经验&#xff0c;能独立做开发&#xff0c;日语口语也好&#xff0c;不需要协助就能独立跟日本人交流的人&#xff…

DeepFace【部署 01】轻量级人脸识别和面部属性分析框架安装使用详解(网盘分享模型文件)

DeepFace安装使用 1.安装1.1 官方的三种方式1.2 使用的方式 2.使用2.1 模型文件下载2.2 Facial Recognition2.3 Face Verification2.4 Face recognition2.5 Embeddings2.6 Face recognition models2.7 Similarity2.8 Facial Attribute Analysis2.9 Face Detectors 3.总结 Githu…

知网G4期刊-基础教育论坛-如何投稿?

《基础教育论坛》知网 3版5000字符 24年上半年刊期&#xff0c;可收中小学基础教育&#xff0c;幼儿教育等教育全科文章。 《基础教育论坛》主要刊登有关教育教学理论探讨及课程改革、教学改革、考试改革研究等方面的文章&#xff0c;为广大基础教育工作者提供学术交流的…

Lua学习笔记:debug.sethook函数

前言 本篇在讲什么 使用Lua的debug.setHook函数 本篇需要什么 对Lua语法有简单认知 依赖Sublime Text工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题 &…

第五章:C语言的数组

文章目录 1、数组的理解2、各类数组的定义3、变长数组4、字符数组 1、数组的理解 一维数组&#xff1a;比如定义一个int a[3];,那么可以将其看成两部分&#xff0c;a【3】为①&#xff0c;int为②。意思就是有一个数组名字为a&#xff0c;里面包含3个&#xff08;池&#xff0…

QT5自定义下拉框为QTreeView类型(树形分上下级)的下拉框(QComboBox)(超详细步骤)

项目开发中&#xff0c;很可能简单的QComboBox满足不了需求&#xff0c;就需要自定义QComboBox。 先看效果。 自定义ComboBox 1、先建立一个project&#xff0c;命名为CustomComboBox,建立一个project的过程不细说了。建立后的工程目录如下图&#xff1a; 2、在项目名CustomCo…

React 全栈体系(十六)

第八章 React 扩展 五、Context 1. 代码 /* index.jsx */ import React, { Component } from react import ./index.css//创建Context对象 const MyContext React.createContext() const {Provider,Consumer} MyContext export default class A extends Component {state …

蓝桥杯每日一题20223.9.26

4407. 扫雷 - AcWing题库 题目描述 分析 此题目使用map等都会超时&#xff0c;所以我们可以巧妙的使用哈希模拟散列表&#xff0c;哈希表初始化为-1首先将地雷读入哈希表&#xff0c;找到地雷的坐标在哈希表中对应的下标&#xff0c;如果没有则此地雷的位置第一次出现&#…

2023彩虹商城自助发卡商城+卡卡云模板+wxstore模板

2023彩虹商城自助发卡商城免授权版卡卡云模板wxstore模板 全新SUP模板/知识付费模板/卡卡云模板&#xff0c;首页美化&#xff0c;登陆页美化&#xff0c;修复了pc端购物车页面显示不正常的问题。

RabbitMQ的工作模式——WorkQueues模式

1.工作队列模式 生产者代码 public class Producer_WorkQueues1 {public static void main(String[] args) throws IOException, TimeoutException {//1.创建连接工厂ConnectionFactory factory new ConnectionFactory();//2.设置参数factory.setHost("172.16.98.133&qu…