POJ 2991 Crane

摘要:
POJ_2991如果我们旋转其中一个线段β角,则该线段上方的所有线段都将旋转β角,这与线段树中向间隔添加常数的问题非常相似,因此最好在线段树中考虑它。对于第二个问题,我们可以使用数组度数[i]来表示第i个向量和第i-1个向量之间的当前角度,从而获得当前状态。在每次读取操作之后,我们可以很容易地获得与旋转角度相等的操作,然后更新度数[i]。此外,我们读取的每个操作只会影响一个度数[]的值,而不会影响其他度数[]。

POJ_2991

    如果我们将其中某一个线段旋转β角,那么这个线段上方的所有线段都会旋转β角,这就很类似线段树中的对区间加上一个常数的问题了,于是不妨向着线段树的思路去想。

    接下来一个问题就是β角是相对于谁的,换句话说我们所谓的每个线段都会旋转β角,那么是绕谁旋转的?实际上,如果我们局限于把线段当线段看的话,那么这个旋转就会看成是绕某个定点的,这个点就是我们旋转的线段和它下面那个不动的线段的交点,再这样想下去我们就没法处理了,因为每个旋转操作所绕的定点不是唯一的,我们没办法把所有的旋转操作都统一到一起,那么我们就没办法把旋转操作叠加,这样就没法使用线段树了。

    但如果换个思路的话,实际上β角还等于这个线段旋转后所在的直线和未旋转前所在的直线的夹角,而直线的夹角是可以用向量的夹角表示的,如果我们把线段看成一个向量的话那么β角就是这个向量旋转的角度。如果这么看的话,所有的旋转操作就可以统一到一起了,也可以叠加了,因为这样不会局限于绕哪个定点,只需要把向量自身旋转一下就OK。其实说到这里,我们会发现其实上一段的分析从一开始就误入歧途了,我们着眼于了“β角是相对于谁的”,因为相对的东西是不可能统一到一起的,参考系不一样结果就不一样,所以我们要想把每个线段的旋转操作统一到一起,就要去看这些旋转操作改变了哪些绝对的量,而向量就是一个绝对的量,它并不参考与其他的东西,只由这个线段自身的状态决定。

    又扯远了,还是分析下怎么实现吧。前面说了,如果把线段看成向量的话,我们每次的旋转操作就可以看成是对区间上的所有点加上一个值(向量的旋转角),那么剩下的问题有两个:第一,也是最重要的,我们这样记录能不能每次方便地求出终点的位置?第二,题目中给出的是每次设定的两个线段的夹角,我们能不能方便的将其转化成相对于以前的状态旋转了多少角度?

    对于第一个问题,我们既然有了每个线段的向量,那么我们只要把这些向量求和,最终所指的位置就是终点,因此我们只要维护好向量的区间和就可以了。对于第二个问题,我们可以用一个数组degree[i]表示第i个向量和第i-1一个向量当前的夹角,这样就有了当前的状态,每次读入操作后就会方便的得到相当于进行旋转多少角度的操作了,然后再更新一下degree[i]即可。并且我们每读入一个操作只会影响一个degree[]的值,不会影响到其他的degree[]

    总而言之,我们要把每个线段看成一个向量,并维护这些向量的区间和,同时要实现对区间中每个元素都加上一个值这一操作。

#include<stdio.h>
#include<string.h>
#include<math.h>
#define MAXD 10010
const double PI = acos(-1.0);
int N, M, a[MAXD], A[MAXD], degree[MAXD], rd[4 * MAXD];
struct point
{
double x, y;
}dp[4 * MAXD];
double getrad(int x)
{
return x * PI / 180;
}
void build(int cur, int x, int y)
{
int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1;
rd[cur] = 0;
dp[cur].x = 0, dp[cur].y = A[y] - A[x - 1];
if(x == y)
return ;
build(ls, x, mid);
build(rs, mid + 1, y);
}
void init()
{
int i;
A[0] = 0;
for(i = 1; i <= N; i ++)
{
scanf("%d", &a[i]);
A[i] = A[i - 1] + a[i];
degree[i] = 0;
}
build(1, 1, N);
}
void update(int cur)
{
int ls = cur << 1, rs = (cur << 1) | 1;
dp[cur].x = dp[ls].x + dp[rs].x, dp[cur].y = dp[ls].y + dp[rs].y;
}
void Rotate(double &dx, double &dy, double rad)
{
double x = dx, y = dy;
dx = x * cos(rad) - y * sin(rad);
dy = x * sin(rad) + y * cos(rad);
}
void pushdown(int cur)
{
int ls = cur << 1, rs = (cur << 1) | 1;
if(rd[cur])
{
double rad = getrad(rd[cur]);
rd[ls] += rd[cur], rd[rs] += rd[cur];
Rotate(dp[ls].x, dp[ls].y, rad);
Rotate(dp[rs].x, dp[rs].y, rad);
rd[cur] = 0;
}
}
void refresh(int cur, int x, int y, int k, int delta)
{
int mid = (x + y) >> 1, ls = cur << 1, rs = (cur << 1) | 1;
if(x == y)
{
double rad = getrad(delta);
Rotate(dp[cur].x, dp[cur].y, rad);
return ;
}
pushdown(cur);
if(mid + 1 < k)
refresh(rs, mid + 1, y, k, delta);
else
{
double rad = getrad(delta);
if(k <= mid)
refresh(ls, x, mid, k, delta);
Rotate(dp[rs].x, dp[rs].y, rad);
rd[rs] += delta;
}
update(cur);
}
void solve()
{
int i, j, k, d, delta;
for(i = 0; i < M; i ++)
{
scanf("%d%d", &k, &d);
++ k, d = d - 180;
delta = d - degree[k];
degree[k] = d;
refresh(1, 1, N, k, delta);
printf("%.2f %.2f\n", dp[1].x, dp[1].y);
}
}
int main()
{
int t = 0;
while(scanf("%d%d", &N, &M) == 2)
{
init();
if(t ++)
printf("\n");
solve();
}
return 0;
}

 

免责声明:文章转载自《POJ 2991 Crane》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Mysql 服务无法启动 服务没有报告任何错误mongodb的TTL索引介绍(超时索引)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

GIS中的数据库.gdb与.mdb的区别

Gdb是文件地理数据库,mdb是个人地理数据库,两者都是数据库文件类型。个人地理数据库是基于access数据库的个人数据库格式mdb,可存储不超过2G的文件,仅适用于Windows系统;文件数据库是保存在文件系统文件夹中的各种类型的GIS数据集的集合。请参阅文章“GIS中database.gdb和.mdb之间的区别是什么?”,m892832piczpec5。...

RedisTemplate

在SpringBoot@RequestMapping(“/del/{key}”)publicStringdel(@PathVariable(“key”)Stringkey){try{//当该键不存在时,异常redisTemplate.delete(key);return“Success”;}将不会引发catch(Exceptione){returne.get...

【JVM】元空间详解 Metaspace

nocs。JpgNoKlassisMetaspaceNoKlassinMetaspaces专用于存储其他与klass相关的内容,如方法、常量池等。它可以由多个不连续的存储器组成。在元空间GC之后,还将调整阈值。默认情况下,MaxMetaspaceSize基本上是无限的,因为大多数元空间都是在本地内存中分配的,但它仍然受到本地内存大小的限制。为了防止元空间的无...

sqlite3 数据类型 批量插入

SQLite3采用动态数据类型。存储值的数据类型与值本身相关,而不是由其字段类型决定。SQLite3的动态数据类型可以向后兼容其他数据库常用的静态类型,这意味着在使用静态数据类型的数据库中使用的数据表也可以在SQLite3中使用。在SQLite2数据库中,除了声明为主键的INTEGER列外,任何列都可以存储属于任何存储类型的值。...

MarkDown技巧:两种方式实现页内跳转

MarkDown技术:有两种方法可以跳转到页面上的电子邮件地址:JohnTsai.Work@gmail.com,欢迎交流讨论。我喜欢MarkDown简单直观的写作风格。...

postman点击一次连续发送多次请求

可以测试同一个时间点创建订单。因为在工作中遇到的以此记录下,在工作上遇到同一个时间点产生了相同的赛时单号。...