POJ 2991线段树

思路就是按照白皮书上走得,不过代码没有按照白皮书的写,一些关键点直接看代码吧。
学到的公式:向量 以任意(x0,y0)顺时针旋转a角度后新向量
x’=(x-x0)*cos(a)-(y-y0)*sin(a)+x0
y’=(x-x0)*sin(a)+(y-y0)*cos(a)+y0

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<map>
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const long long INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int, int> pir;
int n, co;
double yi[4*10005];
double xi[4*10005];
double d[4*10005];
double degree[4*10005];
double  a;
int s;
void pushup(int rt)	//向上跟新节点,即回溯父节点
{
	xi[rt] = xi[rt << 1] + xi[rt << 1 | 1];
	yi[rt] = yi[rt << 1] + yi[rt << 1 | 1];
}
void build(int l, int r,int rt)//建树
{
	d[rt] = 0;//lazy标记。
	if (l == r)//左右相等的时候,即叶子节点
	{
		scanf("%lf", &yi[rt]);
		xi[rt] = 0;//初始x轴方向一直为零
		return;
	}
	int mid = (l + r) >> 1;
	build(l, mid, rt << 1);
	build(mid + 1, r, rt << 1 | 1);
	pushup(rt);//先递归建树,再更新父节点依次
}

void rotate(int rt, double a)//旋转
{
	double rad = pi * a / 180.0;
	double nx = xi[rt] * cos(rad) - yi[rt] * sin(rad);
	double ny = xi[rt] * sin(rad) + yi[rt] * cos(rad);//向量更新公式
	xi[rt] = nx;
	yi[rt] = ny;
}
void pushdown(int rt)//往自己的左右节点更新,即向下更新
{
	if (d[rt])//如果有标记的话,因为这里我们每一次旋转了过后,值更新了父节点并没有将该节点
	//的子节点更新,因为这样太慢了,只有要用到的时候,在更新
	{
		d[rt * 2] += d[rt];
		d[rt<< 1 | 1] += d[rt];
		rotate(rt<<1, d[rt]);
		rotate(rt << 1 | 1, d[rt]);	
		d[rt] = 0;//清楚标记
	}
}
void solve(int i,int l,int r,double a,int rt)//树的主要函数
{
	if (i <= l)//如果s+1在整个区间的左边了,肯定全部更新
	{
		rotate(rt,a);
		d[rt] += a;
		return;//这里要return,不继续更新子节点,就是lazy标记的作用了,用到再更新
	}
	pushdown(rt);//每一个根节点都要向下更新试试,看是否标记了。
	int mid = (r + l) >> 1;
	if (i <= mid)solve(i, l, mid, a, rt << 1);//注意等号
	solve(i, mid + 1, r, a, rt << 1 | 1);//无论如何右边是要更新的,因为i如果<=mid,那么
	//右边节点递归过去就相当于s+1在整个右边节点,需要整体旋转,如果i>mid那么更要更新右边了
	pushup(rt);//回溯父节点
}
int main()
{
	int flag = 0;
	while (cin >> n >> co)
	{
		if (flag++)puts("");//控制一下输出格式
		build(1, n, 1);
		upd(i, 0, n)degree[i] = 180;
		up(i, 0, co)
		{
			cin >> s >> a;
			solve(s + 1, 1, n, a - degree[s], 1);//第s+1跟旋转
			//这里我全部用的左闭右闭区间
			degree[s] = a;//每次更新与y轴的角度
			printf("%.2f %.2f\n", xi[1], yi[1]);
		}
	}
	return 0;
}
//我这个似乎1600ms了,应该可能有些地方做的不够好,看看还能不能继续优化了

猜你喜欢

转载自blog.csdn.net/weixin_44019404/article/details/88716359