题目链接

原题解:

把每个点的坐标视为复数,那么每次询问就是区间求平均数(先求和然后除以个数)。一个点绕着原点旋转就是乘以$(\cos 60^\circ +i\sin 60^\circ)$。

一个点绕着某点$A$旋转可以理解为先减去点$A$的坐标,再绕原点旋转,再加上点$A$的坐标。

最终变成用线段树维护复数的序列,支持区间加上一个数和乘上一个数和区间求和,使用打标记的线段树解决。

补充:

在?为什么卡空间?

原题解提供的线段树做法很NICE,然而直接上线段树会被卡空间。然后我百度到了深度好文,学习了线段树少开空间的方法。

首先来个结论:

长度为$n$的序列会形成$2n-1$个节点的线段树。

为啥呢?观察一下线段树的结构,发现有底下的$n$个叶子,之后两个子节点用一个父节点合并。由$n$个连通块合并为一个块需要$n-1$次,所以总共有$2n-1$个节点。

然后我们考虑用DFS序给节点编号。 发现对于节点$i$代表$[l,r]$,左儿子的编号就是$i+1$,然后左子树占用了连续的$2(mid-l+1)-1$个编号,那么右儿子编号就是$i+2(mid-l+1)-1+1=i+2(mid-l+1)$。

然后就过了。打标记的线段树参考我自己的这篇博客

噢还有一点,就是浮点数要记得重写一个等于函数,本题因为有封装,不容易注意到这个问题,但这确实会导致RE。

代码(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
typedef double DB;
#define RI RG int
#define RD RG DB
#define RP RG Pr
#define RM RG Mrk
const int N=1e5;
const DB Pi=acos(-1);
const DB eps=1e-6; int n,q; struct Pr{
DB x,y;
Pr(RD x=0,RD y=0):x(x),y(y){}
}mul1=Pr(1,0),add0=Pr(0,0)
,rot=Pr(cos(Pi/3),sin(Pi/3))
,a[N+3],sum[N*2+3]; IL bool eql(RD x,RD y){
return fabs(x-y)<eps;
} IL bool operator==(RP x,RP y){
return eql(x.x,y.x)&&eql(x.y,y.y);
} IL Pr operator*(RP x,RP y){
return Pr(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);
} IL Pr operator*(RD x,RP y){
return Pr(x*y.x,x*y.y);
} IL Pr operator+(RP x,RP y){
return Pr(x.x+y.x,x.y+y.y);
} struct Mrk{
Pr m,a;
Mrk(RP m=mul1,RP a=add0):m(m),a(a){}
}non,tag[N*2+3]; IL bool operator==(RM x,RM y){
return x.m==y.m&&x.a==y.a;
} IL int ls(RI i){return i+1;}
IL int rs(RI i,RI l,RI mid){
return i+((mid-l+1)<<1);
} IL void f(RI i,RI l,RI r,RM opt){
if(opt==non)
return ; sum[i]=opt.m*sum[i]+(DB)(r-l+1)*opt.a; Mrk tmp=tag[i];
tag[i]=Mrk(opt.m*tmp.m,opt.m*tmp.a+opt.a); } IL void spread(RI i,RI l,RI r){
RI mid=(l+r)>>1;
f(ls(i),l,mid,tag[i]);
f(rs(i,l,mid),mid+1,r,tag[i]);
tag[i]=non; } void build(RI i,RI l,RI r){
tag[i]=non;
if(l==r){
sum[i]=a[l];
return ; } RI mid=(l+r)>>1;
build(ls(i),l,mid);
build(rs(i,l,mid),mid+1,r);
sum[i]=sum[ls(i)]+sum[rs(i,l,mid)]; } void mdf(RI i,RI l,RI r,RI ql,RI qr,RM opt){
if(ql<=l&&r<=qr){
f(i,l,r,opt);
return ; } spread(i,l,r);
RI mid=(l+r)>>1;
if(ql<=mid)
mdf(ls(i),l,mid,ql,qr,opt);
if(qr>mid)
mdf(rs(i,l,mid),mid+1,r,ql,qr,opt);
sum[i]=sum[ls(i)]+sum[rs(i,l,mid)]; } Pr qry(RI i,RI l,RI r,RI ql,RI qr){
if(qr<l||r<ql)
return add0;
if(ql<=l&&r<=qr)
return sum[i]; spread(i,l,r);
RI mid=(l+r)>>1;
if(qr<=mid)
return qry(ls(i),l,mid,ql,qr);
if(ql>mid)
return qry(rs(i,l,mid),mid+1,r,ql,qr);
return qry(ls(i),l,mid,ql,qr)
+qry(rs(i,l,mid),mid+1,r,ql,qr); } int main(){
scanf("%d%d",&n,&q);
for(RI i=1;i<=n;i++)
scanf("%lf%lf",&a[i].x,&a[i].y); build(1,1,n);
for(RI i=1,l,r;i<=q;i++){
scanf("%d%d",&l,&r); RP tmp=1.0/(r-l+1)*qry(1,1,n,l,r);
printf("%.10lf %.10lf\n",tmp.x,tmp.y);
mdf(1,1,n,l,r,Mrk(mul1,-1.0*tmp));
mdf(1,1,n,l,r,Mrk(rot,add0));
mdf(1,1,n,l,r,Mrk(mul1,tmp)); } for(RI i=1;i<=n;i++){
a[i]=qry(1,1,n,i,i);
printf("%.10lf %.10lf\n",a[i].x,a[i].y); } return 0; }

最新文章

  1. 2048游戏C语言代码
  2. 二、JavaScript语言--JS实践--信息滚动效果制作
  3. RSA密钥——JAVA与C#的区别和联系
  4. page resizing
  5. TestNG超详细教程
  6. Android 全屏相关操作
  7. myeclipse跟eclipse中使用github做版本控制工具
  8. JavaScript验证身份证号
  9. ##DAY5 UIControl及其子类
  10. Cshap 使用http发起请求.
  11. MVC源码解析 - 进入CLR
  12. 为Ghost博客扩展代码高亮、数学公式、页面统计、评论
  13. Java开发笔记(五)数值变量的类型
  14. 201771010141 周强《面向对象程序设计(java)》第十三周学习总结
  15. C#部分试题实例
  16. LOJ-10102(求A到B之间的割点)
  17. 【Python】【网络编程】
  18. delete/truncate/drop table的区别以及锁在这里的角色
  19. Graphics.Blit
  20. JDBC-DAO经典模式 实现对数据库的增、删、改、查

热门文章

  1. HIVE-CREATE TABLE
  2. 【运行报错】Openstack 在部署 Keystone 时出现依赖包报错 (解决安装时依赖包报错问题)
  3. QT部署安装以及后续更新(一)
  4. IE和FireFox 对FORM enctype属性的认识存在差异
  5. VM虚拟机的创建和CentOS 7的安装
  6. day1 AcWing 836. 合并集合
  7. Linux内核机制—smp_hotplug_thread
  8. 其他计算机&amp;网络&amp;行业知识
  9. QTcpSocket 设置接收数据延时等待时间
  10. 浅析Winform的可视样式