挑战程序竞赛系列(33):POJ 2991 Crane
来源:互联网 发布:时间序列数据集 编辑:程序博客网 时间:2024/06/17 05:02
挑战程序竞赛系列(33):POJ 2991 Crane*
详细代码可以fork下Github上leetcode项目,不定期更新。
练习题如下:
- POJ 2991: Crane
POJ 2991: Crane
一道线段树题,在《挑战》中刷到了,就和之前理解的线段树拿来对比下,关于线段树可以参看【算法细节系列(28):线段树】。
先来分析下题目,对于每个线段的更新,我们的目的在于记录终点的位置,此题的一个trick在于能否想到更新中的相对不变量【向量】,何以理解?
首先,旋转位置s,那么必然终点的位置会发生变化,所带来的结果是s后续的每个顶点都会发生相对变化,而s之间的顶点则没有改变。这种更新模式就特别符合线段树的性质了,因为线段树的提出就是基于对更新的优化,使得它在
这种模拟的做法必然会超时。那么问题来了,如何让这些更新规则去符合线段树的构造规则呢?
就拿上述方案来说,如果每个顶点维护一个坐标变量,那么就无法用区间来表示,因为每个顶点相互独立,它们的信息无法共享,所以区间合并所带来的问题是并不知道选谁作为该区间的代表。理论上来说,是终点坐标,可是却无法从几个孩子结点得到终点坐标的信息,得换换思路了。
向量,每个区间[l,r]维护一个起点指向终点r的向量,这和题目有关,因为我们只需要维护终点信息,而无需关注其它顶点的位置,那么我们就把问题抽象成了区间,每个区间维护一个向量,由此,区间与区间就能根据向量来合并了,合并规则为:当前向量加上右区间向量和所需旋转的角度。
这样一来,根结点必然永远都是从起点指向终点的向量,也就是答案x和y。
具体更新规则为:
代码如下:
import java.util.Scanner;public class Main{ static class SegementTree{ int size; double[] vx; double[] vy; double[] angle; public SegementTree(int n){ this.size = (1 << n) - 1; vx = new double[size]; vy = new double[size]; angle = new double[size]; } public void init(int s, int l, int r){ //对应的区间为[l,r) angle[s] = vx[s] = 0; //叶子结点 和非叶子结点 if (r - l == 1){ vy[s] = len[l]; //表示区间叶子结点与下一段的向量 } else{ int chl = s * 2 + 1; int chr = s * 2 + 2; init(chl, l, (l + r) / 2); //天然的划分所有区间 init(chr, (l + r) / 2, r); vy[s] = vy[chl] + vy[chr]; //对于非叶子结点,vy表示合并后,终点向量距离原点的(x,y) } } //从s开始,更新与s+1的角度,逆时针旋转角度a,当前线段树的结点为v,对应的区间为l和r //由题意得,线段树中的每个左区间的向量需要变换,但右区间作为整体不需要做任何计算。 public void change(int s, double a, int v, int l, int r){ if (s <= l) return; // 如果需要旋转的s,对应当前的区间为右孩子,则不需要更新,因为它们隶属于一个整体 else if (s < r){ int chl = v * 2 + 1, chr = v * 2 + 2; int m = (l + r) / 2; //分治求解 change(s, a, chl, l, m); change(s, a, chr, m, r); //区间合并 if (s <= m) angle[v] += a; // 右儿子需要转动的角度,如果s 在右侧,则只需要更新当前状态ang[v] double sin = Math.sin(angle[v]); double cos = Math.cos(angle[v]); vx[v] = vx[chl] + (cos * vx[chr] - sin * vy[chr]); vy[v] = vy[chl] + (sin * vx[chr] + cos * vy[chr]); } } } static int[] len; static double[] prv; public static void main(String[] args) { Scanner in = new Scanner(System.in); while (in.hasNext()){ int n = in.nextInt(); int c = in.nextInt(); len = new int[n]; prv = new double[n]; for (int i = 0; i < n; ++i){ len[i] = in.nextInt(); prv[i] = Math.PI; } SegementTree tree = new SegementTree(15); tree.init(0, 0, n); for (int i = 0; i < c; ++i){ int s = in.nextInt(); int A = in.nextInt(); double a = A / 360.0 * 2 * Math.PI; tree.change(s, a - prv[s], 0, 0, n); prv[s] = a; System.out.println(String.format("%.2f %.2f", tree.vx[0], tree.vy[0])); } } in.close(); }}
阅读全文
0 0
- 挑战程序竞赛系列(33):POJ 2991 Crane
- 挑战 POJ.2991 Crane
- POJ 2991 Crane 线段树+计算几何 出自“挑战程序设计竞赛”
- 挑战程序竞赛系列(81):4.3 LCA(1)
- 挑战程序竞赛系列(82):4.3 LCA(2)
- 挑战程序竞赛系列(1):2.3动态规划
- 挑战程序竞赛系列(4):2.1深度优先搜索
- 挑战程序竞赛系列(5):2.1广度优先搜索
- 挑战程序竞赛系列(6):2.1穷尽搜索
- 挑战程序竞赛系列(9):2.4优先队列
- 挑战程序竞赛系列(10):2.4并查集
- 挑战程序竞赛系列(11):2.5最短路径
- 挑战程序竞赛系列(12):2.5最小生成树
- 挑战程序竞赛系列(13):2.6辗转相除法
- 挑战程序竞赛系列(14):2.6素数
- 挑战程序竞赛系列(15):2.6快速幂运算
- 挑战程序竞赛系列(16):3.1最大化最小值
- 挑战程序竞赛系列(17):3.1最大化平均值
- VC显示jpg图像
- git 如何获取指定 tag 代码
- VMware Fusion配置Nat静态IP
- Java注解详解
- SPI机制的一次具体使用
- 挑战程序竞赛系列(33):POJ 2991 Crane
- [USACO3.3]亚瑟王的宫殿 Camelot
- Android apk 加固
- 线程阻塞怎么结束
- C++ 快排
- java的path与classpath
- Mysql笔记
- Pandas学习总结(上)
- 常见类---BigDecimal类