poj Crane 线段树变种

今天刚刚看了线段树,以为挺简单,不就就是一个区间的最小值,靠父亲节点来判断~。

谁知,看了后面的一道运用线段树的问题,顿时傻眼了再见,代码看了好多遍,根本就是 就是 就是啥也不懂呗。。。。还能是神马。然后就是老规矩,看网上题解。。。然而这次居然不灵了,还是没怎么懂,然后,然后,然后就是去洗衣服去了呗,还能干神马。神奇的是,在洗衣服过程中,我居然有点想通了。。。。       有点激动地来跟大家分享一下。

 

首先明确各种题解中的坐标不是单个棍子首尾2端点各自的坐标,而是在特定的区间内所有棍子合起来的(可以看作一个向量)的坐标。而这个特定区间其实就是 线段树分成的各个区间呗。。。。(一直平分,直到最后是剩下叶节点),而我们学习的线段树的根节点代表整个区域的最小值,是根据其2个儿子节点的数值,比较得来的,如果改变某一叶节点的值,会相应的改变包含其的所有节点的值。同理:在此题中,根节点的坐标就是所有棍子合起来的坐标呗,它的值是根据其2个儿子节点的数值合起来的呗。。。。。然后往下推一直到叶节点。。。而转动某一根棍子,相当于把线段树中的叶节点的值给改变了呗,咱们就要相应地改变包含这根棍子的所有节点(区域)的值。思路就是这样。。。。。

 

下面代码是书上的。。。。。

#include<stdio.h>
#include<math.h>
#define M_PI acos(-1.0)
#define SIZE 100100
int N,C;
int L[20010],S[20010],A[20010];
double vx[SIZE<<2],vy[SIZE<<2],ang[SIZE<<2],prv[10010];
void init(int k,int l,int r)
{
	int chl,chr;
	ang[k]=vx[k]=0.0;
	if(r-l==1)
		vy[k]=L[l];
	else
	{
		chl=k*2+1;chr=k*2+2;
		init(chl,l,(l+r)/2);
		init(chr,(l+r)/2,r);
		vy[k]=vy[chl]+vy[chr];
	}
}
void change(int s,double a,int v,int l,int r)
{
	int chl,chr,m;
	double ss,c;

	if(s<=l)
		return ;
	else
		if(s<r)
		{
			chl=v*2+1;
			chr=v*2+2;
			m=(l+r)/2;
			change(s,a,chl,l,m);
			change(s,a,chr,m,r);
			if(s<=m)
			{
				ang[v]+=a;
			}
			ss=sin(ang[v]);c=cos(ang[v]);
			vx[v]=vx[chl]+(c*vx[chr]-ss*vy[chr]);
			vy[v]=vy[chl]+(ss*vx[chr]+c*vy[chr]);
		
		}
}
int main(void)
{
	int s,i;
	double a;
	while(scanf("%d%d",&N,&C)!=EOF)
	{
		for(i=0;i<N;i++)
			scanf("%d",&L[i]);
		for(i=0;i<C;i++)
			scanf("%d%d",&S[i],&A[i]);
		init(0,0,N);
		for(i=1;i<N;i++)
			prv[i]=M_PI;
		for(i=0;i<C;i++)
		{
			s=S[i];
			a=A[i]*2*M_PI/360.0;
			change(s,a-prv[s],0,0,N);
			prv[s]=a;
			printf("%.2f %.2f\n",vx[0],vy[0]);
		}
	}
	
}
 
 
 
 
 
#include<stdio.h>
#include<math.h>
#define lc p<<1,s,mid
#define rc p<<1|1,mid+1,e
#define mid ((s+e)>>1)
#define N 10005
#define pi acos(-1.0)
#define eps 1e-8
double x[N>>2],y[N>>2],rot[N>>2];
double rad[N];


void rotate(double &x,double &y,double r)
{
	double xx=x;
	x=xx*cos(r)-y*sin(r);
	y=xx*sin(r)+y*cos(r);
}
void pushup(int p)
{
	x[p]=x[p<<1]+x[p<<1|1];
	y[p]=y[p<<1]+y[p<<1|1];
}
void pushdown(int p,int s,int e)
{
	int lp,rp;
	if(fabs(rot[p])>eps)
		return ;
	lp=p<<1;rp=p<<1|1;
	rot[lp]+=rot[p];
	rot[rp]+=rot[p];
	rotate(x[lp],y[lp],rot[p]);
	rotate(x[rp],y[rp],rot[p]);
	rot[p]=0;
}
void build(int p,int s,int e)
{
	rot[p]=0;
	if(s==e)
	{
		scanf("%lf",&y[p]);
		x[p]=0;
		rad[s]=pi;
		return ;
	}
	build(lc);
	build(rc);
	pushup(p);
}
void update(int p,int s,int e,int l,int r,double v)
{
	if(l<=s&&e<=r)
	{
		rot[p]+=v;
		rotate(x[p],y[p],v);
		return ;
	}
	pushdown(p,s,e);
	if(l<=mid)update(lc,l,r,v);
	if(r>mid)update(rc,l,r,v);
	pushup(p);
}
int main(void)
{
	int n,m,p,first=1;
	double r;
	while(~scanf("%d%d",&n,&m))
	{
		build(1,1,n);
		while(m--)
		{
			scanf("%d%lf",&p,&r);
			r=r/180.0*pi;
			update(1,1,n,p+1,n,r-rad[p]);
			rad[p]=r;
			printf("%.2lf %.2lf\n",x[1],y[1]);
		}

	}

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值