拼接游戏(线段树、计算几何)

题目大意:

  给出一段由N个线段首尾相接而成的绳子,其中第 i 条线段(从1开始)是由点 Pi-1 和 点 Pi 相连而成,一开始绳子从原点向y轴正方向延伸,现在要对绳子做M次修改,每次修改会把第u + 1条线段调整到第u条线段逆时针方向w度的位置,其他线段之间的关系不变,要求输出每次修改后点 PN 的坐标。

  输入:第一行N,M(N,M <= 10000);第二行N个整数,表示这些线段的长度(长度为1~99之间的整数);接下来M行每行两个整数u,w(0 < u < N,0 <= w < 360)。

  输出:对于每次修改输出一行,包含两个两位小数,表示点 PN 的坐标。

分析:

  通过观察可以发现,当第k条线段旋转了x度,其他线段之间角度关系不变的情况下,第k + 1 ~ N条线段相对于第k条线段没有发生改变,而相对于平面直角坐标系则是旋转了x度,且在第k + 1 ~ N条线段中,任意多个线段组成的图形修改前后形状没有发生改变,且相对于修改之前旋转了x度!把所有的线段用向量(x,y)来表示,那么每次操作就是将第k ~ N条线段进行旋转,然后输出所有线段表示的向量的和。

  因为N,M <= 10000,很显然会想到用NlogN的做法,而通过上面的分析,那么这个问题就转换成了区间修改和区间求和,修改会麻烦一点,因为不同的线段改的都不一样,但是有一个共同点就是旋转的角度一样,用线段树来维护,lazy-tag中记录角度就好了。线段树每个节点的x,y表示该节点所代表的区间的所有的向量的和,也可以把它看作一个向量。

  (实际上这道题的时限是2s,N^2暴力做是可以过的,需要预处理出所有的sin和cos。)

  出现的错误:

    1、精度误差:虽然只让输出两位小数,但还是会出现精度误差,错误的原因在于pi,pi要等于acos(-1)而不能手动输入。(学长就栽在这里了。。)

    2、角度计算:调用atan(x,y)来计算向量(x,y)与坐标轴的夹角时,需要对x、y进行正负的判断。(调了一天才调出来QAQ)

代码:

 1 #include <cstdio>
 2 #include <cmath>
 3 using namespace std;
 4 const double pi = acos (-1);
 5 struct tree {
 6     double x, y, lazy;
 7 } t[1000000];
 8 int n, m, u, w, len[10010];
 9 double degree (double ox, double oy) {
10     return atan (ox / oy) + (oy < 0 ? pi : 0);
11 }
12 double turn (double deg, double &ox, double &oy) {
13     double tx = ox * cos (deg) - oy * sin (deg);
14     double ty = ox * sin (deg) + oy * cos (deg);
15     ox = tx, oy = ty;
16 }
17 inline void pushup (int o) {
18     t[o].x = t[o << 1].x + t[o << 1 | 1].x;
19     t[o].y = t[o << 1].y + t[o << 1 | 1].y;
20 }
21 inline void pushdown (int o) {
22     int l = o << 1, r = o << 1 | 1;
23     double deg = t[o].lazy;
24     turn (deg, t[l].x, t[l].y); t[l].lazy += deg;
25     turn (deg, t[r].x, t[r].y); t[r].lazy += deg;
26     t[o].lazy = 0;
27 }
28 void build (int i, int left, int right) {
29     if (left < right) {
30         int mid = (left + right) >> 1;
31         build (i << 1, left, mid);
32         build (i << 1 | 1, mid + 1, right);
33         pushup (i);
34     }else t[i].x = 0, t[i].y = len[left];
35 }
36 int mleft, mright;
37 double mdeg, mx, my;
38 void modify (int i, int l, int r, int mid) {
39     if (mleft <= l && mright >= r) {
40         turn (mdeg, t[i].x, t[i].y);
41         t[i].lazy += mdeg; return;
42     }
43     pushdown (i);
44     if (mleft <= mid && mright >= l)
45         modify (i << 1, l, mid, (l + mid) >> 1);
46     if (mright > mid && mleft <= r)
47         modify (i << 1 | 1, mid + 1, r, (r + mid + 1) >> 1);
48     pushup (i);
49 }
50 void mturn (int left, int right, double deg) {
51     mleft = left, mright = right; mdeg = deg;
52     modify (1, 1, n, (1 + n) >> 1);
53 }
54 void ask (int i, int l, int r, int mid) {
55     if (mleft <= l && mright >= r) {
56         mx += t[i].x, my += t[i].y;
57         return;
58     }
59     pushdown (i);
60     if (mright >= l && mleft <= mid)
61         ask (i << 1, l, mid, (l + mid) >> 1);
62     if (mleft <= r && mright > mid)
63         ask (i << 1 | 1, mid + 1, r, (r + mid + 1) >> 1);
64 }
65 void askxy (int left, int right) {
66     mleft = left, mright = right; mx = my = 0;
67     ask (1, 1, n, (1 + n) >> 1);
68     if (abs (mx) <= 0.001) mx = 0;
69     if (abs (my) <= 0.001) my = 0;
70 }
71 int main () {
72     scanf ("%d %d", &n, &m);
73     for (int i = 1; i <= n; i++)
74         scanf ("%d", &len[i]);
75     build (1, 1, n);
76     for (int i = 0; i < m; i++) {
77         scanf ("%d %d", &u, &w);
78         askxy (u, u);
79         double deg = degree (mx, my);
80         askxy (u + 1, u + 1);
81         deg = pi * w / 180 - (deg + pi - degree (mx, my));
82         mturn (u + 1, n, deg);
83         askxy (1, n);
84         printf ("%.2lf %.2lf\n", mx, my);
85     }
86 }

题目来源:学长出的训练题

时间: 2024-08-10 21:22:59

拼接游戏(线段树、计算几何)的相关文章

转载::POJ 2991 线段树+计算几何(有c++结构体操作)

POJ 2991 线段树+计算几何 (2011-02-27 21:13:44) 转载▼ 标签: 杂谈 分类: OI 话说这一题真的是很恶心很恶心,不过确实改变了我对线段树的一些看法,算是很经典的题目. 题意:有一个吊车由很多个不同长度的线段组成,一开始是一条长直线起点在(0,0),尾节点在(0,sum[n]),每条线段之间的夹角的初始值是180度.然后有一些操作a. b将第a条线段和a+1之间的夹角变成b度,经过每一次操作都要求出尾节点的坐标. 首先要用到一个计算几何的知识(没学过..请教而来)

POJ - 2991 Crane (线段树+计算几何)

Description ACM has bought a new crane (crane -- je?áb) . The crane consists of n segments of various lengths, connected by flexible joints. The end of the i-th segment is joined to the beginning of the i + 1-th one, for 1 ≤ i < n. The beginning of t

POJ 2991 Crane(线段树+计算几何)

POJ 2991 Crane 题目链接 题意:给定一个垂直的挖掘机臂,有n段,现在每次操作可以旋转一个位置,把[s, s + 1]专程a度,每次旋转后要输出第n个位置的坐标 思路:线段树,把每一段当成一个向量,这样每一段的坐标就等于前几段的坐标和,然后每次旋转的时候,相当于把当前到最后位置全部加上一个角度,这样就需要区间修改了,然后每次还需要查询s,和s + 1当前的角度,所以需要单点查询,这样用线段树去维护即可 代码: #include <cstdio> #include <cstri

洛谷1558 色板游戏 线段树

我先立个Flag 我,这几天,要过1W道线段树题. 题目背景 阿宝上学了,今天老师拿来了一块很长的涂色板. 题目描述 色板长度为L,L是一个正整数,所以我们可以均匀地将它划分成L块1厘米长的小方格.并从左到右标记为1, 2, ... L.现在色板上只有一个颜色,老师告诉阿宝在色板上只能做两件事:1. "C A B C" 指在A到 B 号方格中涂上颜色 C.2. "P A B" 指老师的提问:A到 B号方格中有几种颜色.学校的颜料盒中一共有 T 种颜料.为简便起见,我

FOJ 1962 新击鼓传花游戏 线段树

维护一个sum数组,有点划分树的思想,写过划分树的应该能看出来 #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> #include<vector> #include<stack> #include<cmath> #include<queue> #include<map> using namespace

POJ2991 Crane 【线段树+计算几何】

Crane Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3777   Accepted: 1031   Special Judge Description ACM has bought a new crane (crane -- je?áb) . The crane consists of n segments of various lengths, connected by flexible joints. The

P1558 色板游戏 线段树(区间修改,区间查询)

题意: 给n,m,k,n长度,k个操作,m种颜色 操作C:输入A,B,C,区间[A,B]变成C颜色,可能A>B,所以要确保A<B 操作P:输入A,B,区间[A,B]的颜色种类 思路: 因为颜色只有30种,可以用位运算,然后进行lazy标记 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define il inline 5 #define it register int 6 #de

&quot;盛大游戏杯&quot; M 风力观测 (线段树)

题目链接:"盛大游戏杯" M 风力观测 题意: 给你n个数,现在有m个操作. 1 L R V 给[L,R]区间全部加V. 2 X 询问X这个点 历史的绝对值最大是多少. 题解: 对于每个询问,其实我们只需要记录下在这个过程中最大的偏移量和最小的偏移量就行了. 所以直接一个线段树搞一搞就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<<1|1 4 #d

【BZOJ-2892&amp;1171】强袭作战&amp;大sz的游戏 权值线段树+单调队列+标记永久化+DP

2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] Description 在一个没有冬马的世界里,经历了学园祭后的春希着急着想要见到心爱的雪菜.然而在排队想见雪菜的fans太多了,春希一时半会凑不到雪菜面前. 作为高帅富,这样的问题怎么能难倒春希?春希从武也手中拿到了取自金闪闪宝库里的多啦A梦的传话筒,并且给每一个排队的fans都发了一个传话筒. 于