博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
POJ 2991 Crane
阅读量:6464 次
发布时间:2019-06-23

本文共 3226 字,大约阅读时间需要 10 分钟。

POJ_2991

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

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

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

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

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

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

#include
#include
#include
#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; }

 

转载地址:http://vrhzo.baihongyu.com/

你可能感兴趣的文章
轮播插件swiper.js?
查看>>
网路流24题总结
查看>>
15 个 Android 通用流行框架大全
查看>>
IE8兼容@media和mp4视频的解决方案
查看>>
第二周总结
查看>>
【转】知道这20个正则表达式,能让你少写1,000行代码
查看>>
自定义 启动和关闭 oracle 的命令
查看>>
Quartz
查看>>
正则表达式介绍
查看>>
初识Scala反射
查看>>
第三十九天
查看>>
Redis详解
查看>>
论程序员加班的害处
查看>>
codeblocks快捷键
查看>>
基于HTML5的WebGL设计汉诺塔3D游戏
查看>>
WPF资料链接
查看>>
过滤DataTable表中的重复数据
查看>>
再次更新
查看>>
mysql的数据类型int、bigint、smallint 和 tinyint取值范围
查看>>
利用网易获取所有股票数据
查看>>