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