拼接游戏
题目大意:
给出一段由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表示该节点所代表的区间的所有的向量的和,也可以把它看作一个向量。
代码:
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 }
Your eyes light up the world when you smile.