前言
CSP 还是一如既往的码农题,这要是在考场上恐怕是根本写不完。花了一天时间把这套题做了,感觉不是特别难,就是码量太大了。
T1 归一化
平均数和方差应该还是很好求的吧,时间复杂度 ,(方差=平方的平均数-平均数的平方)。
#include <cmath>
#include <cstdio>
const int maxn = 1000 + 1;
double arr[maxn], sum, sum_sqr, ave, stv;
int main() {
int n; scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
double x; scanf("%lf", &x);
arr[i] = x;
sum += x;
sum_sqr += x * x;
}
ave = sum/n;
stv = sqrt(sum_sqr/n - ave*ave);
for(int i = 1; i <= n; i ++) {
printf("%.10f\n", (arr[i] - ave)/stv);
}
return 0;
}
T2 寻宝!大冒险!
由于藏宝图是大地图的一部分,可以考虑枚举藏宝图的左下端点,由于左下端一定有一颗树,所以只要考虑所有树的坐标作为左下端点即可。样例二非常友善地给出了一个细节:要记得判断藏宝图最大坐标和地图最大坐标的关系,藏宝图中给出的实际地图上的最大坐标一定小于等于地图给出的最大坐标。
#include <cstdio>
const int maxn = 1000 + 1;
int n, L, S;
int posx[maxn], posy[maxn]; // position of trees
int G[maxn][maxn]; // graph
int _G[maxn][maxn]; // sub graph
#define rin(A, B, C) (((A)<=(B))&&((B)<=(C)))
bool check(int id) {
int px = posx[id], py = posy[id];
if(px + S > L || py + S > L) { //! attention
return false;
}
for(int i = 0; i <= S; i ++) { // clear sub graph
for(int j = 0; j <= S; j ++) {
_G[i][j] = 0;
}
}
for(int i = 1; i <= n; i ++) {
int nx = posx[i] - px;
int ny = posy[i] - py; // relevant position
if(rin(0, nx, S) && rin(0, ny, S)) {
_G[nx][ny] = 1;
}
}
for(int i = 0; i <= S; i ++) {
for(int j = 0; j <= S; j ++) {
if(G[i][j] != _G[i][j])
return false;
}
}
return true;
}
int main() {
scanf("%d%d%d", &n, &L, &S);
for(int i = 1; i <= n; i ++) {
scanf("%d%d", &posx[i], &posy[i]);
}
for(int i = S; i >= 0; i --) {
for(int j = 0; j <= S; j ++) {
scanf("%d", &G[i][j]);
}
}
int cnt = 0; // count of posible positions
for(int id = 1; id <= n; id ++) {
if(check(id)) cnt += 1;
}
printf("%d\n", cnt);
return 0;
}
T3 角色授权
码农题,一顿 即可,可以用 记录各种对应关系,时间复杂度大概是 级别。使用 cin/cout 要记得写 ios::sync_with_stdio(false),不然会 ,
#include <iostream>
#include <map>
#include <set>
#include <string>
typedef std::set<std::string> List;
/* 检查元素是否位于 List 中 */
bool Exist(const List& list, std::string term) {
return list.find(term) != list.end();
}
/* 合并 List 到 List */
void Insert(List& listTo, const List& listFrom) {
for(const std::string& item: listFrom) {
listTo.insert(item);
}
}
class CharacterDescription { // 角色描述符
private:
std::string m_Name ; // 角色名称
List m_OpeList ; // 角色操作集合
List m_ResTypeList; // 角色资源类集合
List m_ResNameList; // 角色资源名称集合
public:
std::string GetName() const {
return m_Name;
}
void input() { // 从键盘输入描述符
std::cin >> m_Name;
/* 输入操作清单 */
int nv; std::cin >> nv;
while(nv --) {
std::string ope; std::cin >> ope;
m_OpeList.insert(ope);
}
/* 输入资源种类清单 */
int no; std::cin >> no;
while(no --) {
std::string typ; std::cin >> typ;
m_ResTypeList.insert(typ);
}
/* 输入资源名称清单 */
int nn; std::cin >> nn;
while(nn --) {
std::string nam; std::cin >> nam;
m_ResNameList.insert(nam);
}
}
/* 检查角色是否能完成某个操作 */
bool OpeCheck(std::string opeName,
std::string resType, std::string resName) const {
return
(Exist(m_OpeList , "*") || Exist(m_OpeList , opeName)) &&
(Exist(m_ResTypeList, "*") || Exist(m_ResTypeList, resType)) &&
(m_ResNameList.empty() || Exist(m_ResNameList, resName));
}
};
/* 从用户或用户组映射到角色名 */
std::map<std::string, List> g_UserNameToCharName;
std::map<std::string, List> g_UserGroupToCharName;
/* 从角色名映射到描述符 */
std::map<std::string, CharacterDescription> g_CharNameToDescription;
/* 检查角色集合是否可以进行操作 */
bool CharListAvailable(const List& charList,
std::string ope, std::string resType, std::string resName) {
/* 考虑所有的角色名 charName */
for(const std::string& charName: charList) {
/* 获取得到该角色的描述符 */
const CharacterDescription& charDesc = g_CharNameToDescription[charName];
/* 只要任意一个角色能够进行该操作,操作成功 */
if(charDesc.OpeCheck(ope, resType, resName)) {
return true;
}
}
return false;
}
int main() {
std::ios::sync_with_stdio(false);
int N, M, Q; std::cin >> N >> M >> Q;
/* 输入所有角色描述符 */
for(int i = 1; i <= N; i ++) {
CharacterDescription cd; cd.input();
g_CharNameToDescription[cd.GetName()] = cd;
}
/* 输入所有角色关联 */
for(int i = 1; i <= M; i ++) {
std::string charName; std::cin >> charName;
int ns; std::cin >> ns;
while(ns --) {
std::string type; std::cin >> type;
std::string name; std::cin >> name;
if(type == "u") {
/* 建立用户到角色的关系映射 */
g_UserNameToCharName[name].insert(charName);
}else { // type == "g"
g_UserGroupToCharName[name].insert(charName);
}
}
}
/* 输入所有操作对应的序列 */
for(int i = 1; i <= Q; i ++) {
List charList = {};
std::string usrName; std::cin >> usrName; // 用户名称
/* 合并用户所在关联 */
Insert(charList, g_UserNameToCharName[usrName]);
/* 合并用户组所在关联 */
int ng; std::cin >> ng;
while(ng --) {
std::string usrGroup; std::cin >> usrGroup;
Insert(charList, g_UserGroupToCharName[usrGroup]);
}
/* 输入操作名,资源类,资源名 */
std::string ope, resType, resName;
std::cin >> ope >> resType >> resName;
/* 判断是否可以进行操作 */
if(CharListAvailable(charList, ope, resType, resName)) {
std::cout << "1" << std::endl;
}else {
std::cout << "0" << std::endl;
}
}
}
T4 光线追踪
由于 ,因此我们可以断言光线的最大有效反射次数不会超过 次。可以每行/每列使用一个 或者 记录每个镜子的位置以及朝向,然后使用二分法寻找面前的第一面镜子即可,时间复杂度约为 。 大佬提供给的做法只使用了两个 ,时间效率更优秀。
我的做法
#include <algorithm>
#include <cassert>
#include <iostream>
#include <map>
#include <set>
/* 描述光的强度 */
typedef long long LInt;
struct LightPos {
LInt x, y, I; // 光线末端到达的位置以及光的强度
};
/* 描述镜子的法线方向 */
struct MirrorItem {
// const int dx = 1;
int dy; // dy 可以等于 +1 或者 -1
double a; // 反射率
};
/* 描述四个方向 */
const int dirx[] = {+1, 0,-1, 0};
const int diry[] = { 0,+1, 0,-1};
const int DIR_EAST = 0, DIR_NORTH = 1, DIR_WEST = 2, DIR_SOUTH = 3;
/* 计算反射后的方向 */
int NextDir(const int dir_now, const MirrorItem& mirror) {
if(mirror.dy == +1) {
switch(dir_now) {
case DIR_EAST : return DIR_SOUTH;
case DIR_NORTH: return DIR_WEST ;
case DIR_WEST : return DIR_NORTH;
case DIR_SOUTH: return DIR_EAST ;
default:
assert(false /* Dir Not Exist*/ );
}
}else { // mirror.dy == -1
switch(dir_now) {
case DIR_EAST : return DIR_NORTH;
case DIR_NORTH: return DIR_EAST ;
case DIR_WEST : return DIR_SOUTH;
case DIR_SOUTH: return DIR_WEST ;
default:
assert(false /* Dir Not Exist*/ );
}
}
}
/* 物理引擎模拟器 */
namespace Engine {
/* 插入一个镜面 */
void Insert(int id, int x1, int y1, int x2, int y2, double a);
/* 删除操作 id 所插入的镜面 */
void Delete(int id);
/* 计算光线的强度 */
LightPos SolveLight(int x, int y, int d, double I, int t);
/* 分别描述与 X坐标已知 和 Y坐标已知的 平行于轴的直线 */
typedef std::map<int, MirrorItem> MirrorList;
std::map<int, MirrorList> LineX;
std::map<int, MirrorList> LineY;
/* 记录每一次询问 */
const int maxn = 1e5 + 6;
struct QueryRecord { int x1, y1, x2, y2; } qs[maxn];
/* 插入或删除的公共过程 */
void _Insert(int id, int x1, int y1, int x2, int y2, double a, bool insert) {
if(insert)
qs[id] = (QueryRecord){ x1, y1, x2, y2 };
if(x1 > x2) { // 保证 x1 < x2
std::swap(x1, x2); std::swap(y1, y2);
}
/* 对信息进行记录 */
MirrorItem mi;
mi.dy = y1 < y2 ? -1 : +1; // 记录法线方向
mi.a = a;
for(int x = x1 + 1, y = y1 - mi.dy; x < x2; x ++, y -= mi.dy) {
if(insert) {
LineX[x][y] = mi; // 插入
LineY[y][x] = mi;
}else {
LineX[x].erase(LineX[x].find(y)); // 删除
LineY[y].erase(LineY[y].find(x));
}
}
}
void Insert(int id, int x1, int y1, int x2, int y2, double a) {
_Insert(id, x1, y1, x2, y2, a, true);
}
void Delete(int id) {
_Insert(id, qs[id].x1, qs[id].y1, qs[id].x2, qs[id].y2, 0, false);
}
bool GetMirror(LInt x, LInt y, const int Dnow, LInt& NxtX, LInt& NxtY, LInt& Dis, int& NxtDir, double& A) {
typedef MirrorList::iterator ITR;
ITR nxtItr;
switch(Dnow) {
case DIR_EAST: { // y 不变 x 增加
nxtItr = LineY[y].upper_bound(x);
if(nxtItr == LineY[y].end()) {
return false;
}
NxtX = nxtItr -> first;
NxtY = y;
break;
}
case DIR_NORTH: { // x 不变 y 增大
nxtItr = LineX[x].upper_bound(y);
if(nxtItr == LineX[x].end()) {
return false;
}
NxtX = x;
NxtY = nxtItr -> first;
break;
}
case DIR_WEST: { // y 不变 x 变小
nxtItr = LineY[y].lower_bound(x);
if(nxtItr == LineY[y].begin()) {
return false;
}
nxtItr --;
NxtX = nxtItr -> first;
NxtY = y;
break;
}
case DIR_SOUTH: { // y 变小 x 不变
nxtItr = LineX[x].lower_bound(y);
if(nxtItr == LineX[x].begin()) {
return false;
}
nxtItr --;
NxtX = x;
NxtY = nxtItr -> first;
break;
}
default:
assert(false /* Dir not found */ );
}
NxtDir = NextDir(Dnow, nxtItr -> second);
A = (nxtItr -> second).a;
Dis = abs(x - NxtX) + abs(y - NxtY);
return true;
}
/* 暴力计算光线路径 */
LightPos SolveLight(int _x, int _y, int _d, double _I, int _t) {
double I = _I;
int D = _d; // 当前光线方向
LInt x = _x, y = _y, t = _t; // 当前位置
while(t > 0) {
/* 找到移动方向上的下一面镜子以及距离 */
LInt NxtX, NxtY, Dis; int NxtDir;
double A;
bool hasNxt = GetMirror(x, y, D, NxtX, NxtY, Dis, NxtDir, A);
if(hasNxt && t >= Dis) { // 能够走到下一面镜子
/* 移动到下一面镜子 */
t -= Dis ;
x = NxtX ;
y = NxtY ;
D = NxtDir;
I *= A ;
/* 按照转向算法和衰减算法进行衰减 */
if((LInt) I == 0) { // 衰减没了
break;
}
}else {
/* 之后的运动不会再碰到镜子 */
x += (LInt)dirx[D] * t;
y += (LInt)diry[D] * t;
t = 0;
}
}
/* 光强度 = 0 时不考虑位置 */
LightPos Ans = (LightPos){ x, y, (LInt)I };
if(Ans.I == 0) {
Ans.x = Ans.y = 0;
}
return Ans;
}
}
int main() {
/* 关闭同步 */
std::ios::sync_with_stdio(false);
/* 输入所有操作 */
int M; std::cin >> M;
for(int i = 1; i <= M; i ++) {
int op; std::cin >> op;
// std::cerr << "DEBUG " << "op = " << op << " i = " << i << std::endl;
switch(op) {
/* 添加镜面 */
case 1: {
int x1, y1, x2, y2; double a; std::cin >> x1 >> y1 >> x2 >> y2 >> a;
Engine::Insert(i, x1, y1, x2, y2, a); break;
}
/* 删除镜面 */
case 2: {
int k; std::cin >> k;
Engine::Delete(k); break;
}
/* 查询光线 */
case 3: {
int x, y, d; double I; int t; std::cin >> x >> y >> d >> I >> t;
LightPos Ans = Engine::SolveLight(x, y, d, I, t);
std::cout << Ans.x << " " << Ans.y << " " << Ans.I << std::endl;
break;
}
default:
assert(false /* OP Not Exist*/ );
}
}
return 0;
}
ZXX 大佬的做法
#include<bits/stdc++.h>
using namespace std;
#define maxn 100005
#define inf 1000000005
int insx[maxn],insy[maxn];
struct nodex {
int x,y;
double p;
int dir;// 斜率
bool friend operator < (nodex a,nodex b) {
if(a.x!=b.x)return a.x<b.x;
else return a.y<b.y;
}
};
struct nodey {
int x,y;
double p;
int dir;
bool friend operator < (nodey a,nodey b) {
if(a.y!=b.y)return a.y<b.y;
else return a.x<b.x;
}
};
struct data {
int x1,y1,x2,y2;
double p;
int dir;
data() {
}
data(int x1,int y1,int x2,int y2,double p,int dir) {
this->x1=x1;
this->y1=y1;
this->x2=x2;
this->y2=y2;
this->p=p;
this->dir=dir;
}
} input[maxn];
// x1 y1 x2 y2 p dir
set<nodex>sx;
set<nodey>sy;
void print() {
cout<<"PRINT SX:"<<endl;
for(auto it: sx) {
cout<<it.x<<" "<<it.y<<" "<<it.p<<" "<<it.dir<<endl;
}
cout<<"PRINT SY:"<<endl;
for(auto it: sy) {
cout<<it.x<<" "<<it.y<<" "<<it.p<<" "<<it.dir<<endl;
}
}
void calc(int x,int y,int dir,double I,int t) {
while(I>=1.0) {
// cout<<"NOW: X="<<x<<" Y="<<y<<" DIR="<<dir<<" I="<<I<<" T="<<t<<endl;
if(dir==0) {
//x轴正向
auto it = sy.upper_bound({x,y,0,0});
if(it->y!=y) {
printf("%d %d %d\n",x+t,y,(int)I);
return;
} else {
if(t<it->x-x) {
printf("%d %d %d\n",x+t,y,(int)I);
return;
} else {
t-=it->x-x;
x=it->x;
dir=(it->dir==1)?1:3;
I=I*it->p;
}
}
} else if(dir==1) {
//y轴正向
auto it = sx.upper_bound({x,y,0,0});
if(it->x!=x) {
printf("%d %d %d\n",x,y+t,(int)I);
return;
} else {
if(t<it->y-y) {
printf("%d %d %d\n",x,y+t,(int)I);
return;
} else {
t-=it->y-y;
y=it->y;
dir=(it->dir==1)?0:2;
I=I*it->p;
}
}
} else if(dir==2) {
//x轴负向
auto it = sy.lower_bound({x,y,0,0});
it--;
if(it->y!=y) {
printf("%d %d %d\n",x-t,y,(int)I);
return;
} else {
if(t<x-it->x) {
printf("%d %d %d\n",x-t,y,(int)I);
return;
} else {
t-=x-it->x;
x=it->x;
dir=(it->dir==1)?3:1;
I=I*it->p;
}
}
} else {
//y轴负向 dir=3
auto it = sx.lower_bound({x,y,0,0});
it--;
if(it->x!=x) {
printf("%d %d %d\n",x,y-t,(int)I);
return;
} else {
if(t<y-it->y) {
printf("%d %d %d\n",x,y-t,(int)I);
return;
} else {
t-=y-it->y;
y=it->y;
dir=(it->dir==1)?2:0;
I=it->p*I;
}
}
}
}
puts("0 0 0");
}
int main() {
int m;
scanf("%d",&m);
sx.insert({-inf,-inf,0,0});
sx.insert({inf,inf,0,0});
sy.insert({-inf,-inf,0,0});
sy.insert({inf,inf,0,0});
for(int mm=1; mm<=m; mm++) {
int opt;
scanf("%d",&opt);
if(opt==1) {
int x1,y1,x2,y2;
double p;
scanf("%d%d%d%d%lf",&x1,&y1,&x2,&y2,&p);
if(x1>x2)swap(x1,x2),swap(y1,y2);
// x1 < x2
if(y1<y2) {
for(int i=x1+1,j=y1+1; i<=x2-1; i++,j++)
sx.insert({i,j,p,1}),
sy.insert({i,j,p,1});
input[mm]=data(x1,y1,x2,y2,p,1);
} else { // y1 > y2
for(int i=x1+1,j=y1-1; i<=x2-1; i++,j--)
sx.insert({i,j,p,-1}),
sy.insert({i,j,p,-1});
input[mm]=data(x1,y1,x2,y2,p,-1);
}
} else if(opt == 2) {
int k;
scanf("%d",&k);
int dir = input[k].dir;
if(dir==1) {
for(int i=input[k].x1+1,j=input[k].y1+1; i<=input[k].x2-1; i++,j++) {
sx.erase({i,j,input[k].p,1});
sy.erase({i,j,input[k].p,1});
}
} else {
for(int i=input[k].x1+1,j=input[k].y1-1; i<=input[k].x2-1; i++,j--) {
sx.erase({i,j,input[k].p,-1});
sy.erase({i,j,input[k].p,-1});
}
}
} else {
//3 x y d I t
int x,y,dir,t;
double I;
scanf("%d%d%d%lf%d",&x,&y,&dir,&I,&t);
calc(x,y,dir,I,t);
}
// print();
}
return 0;
}
T5 PS无限版
经典的线段树维护区间和,支持区间乘以一个数,只不过这里的数都是矩阵。用一个六维列向量 表示坐标 ,不难发现题中提到的所有操作都是对这个向量进行线性变换。由于线性变换具有结合律,因此可以用区间数据结构维护。
#include <iostream>
#include <iomanip>
#include <cassert>
#include <cmath>
/* 描述矩阵运算 */
const int maxM = 6 + 1;
struct Matrix {
int n, m; // n 行 m 列矩阵
double A[maxM][maxM];
};
/* 计算矩阵乘法 */
Matrix MatMul(const Matrix& A, const Matrix& B) {
assert(A.m == B.n);
Matrix Ans = {A.n, B.m};
for(int i = 1; i <= A.n; i ++) {
for(int j = 1; j <= B.m; j ++) {
double tmp = 0;
for(int k = 1; k <= A.m; k ++) {
tmp += A.A[i][k] * B.A[k][j];
}
Ans.A[i][j] = tmp;
}
}
return Ans;
}
/* 为矩阵乘法重载运算符 */
Matrix operator*(Matrix A, Matrix B) {
return MatMul(A, B);
}
/* 计算矩阵加法 */
Matrix MatAdd(Matrix A, Matrix B) {
assert(A.n == B.n && A.m == B.m);
Matrix Ans = {A.n, B.m};
for(int i = 1; i <= A.n; i ++) {
for(int j = 1; j <= B.n; j ++) {
Ans.A[i][j] = A.A[i][j] + B.A[i][j];
}
}
return Ans;
}
/* 为矩阵加法运算重载运算符 */
Matrix operator+(Matrix A, Matrix B) {
return MatAdd(A, B);
}
/* 用于生成各种计算矩阵 */
namespace MatrixGen {
Matrix Node(double x, double y) { // 生成一个结点向量
Matrix tmp = {6, 1, // 列向量
{
{}, // line 0
{0, x*x}, // line 1
{0, y*y}, // line 2
{0, x*y}, // line 3
{0, x}, // line 4
{0, y}, // line 5
{0, 1} // line 6
}
};
return tmp;
}
Matrix Eye() { // 单位矩阵
static const Matrix EYE = {6, 6, // 单位矩阵
{
{}, // line 0
{0, 1, 0, 0, 0, 0, 0}, // line 1
{0, 0, 1, 0, 0, 0, 0}, // line 2
{0, 0, 0, 1, 0, 0, 0}, // line 3
{0, 0, 0, 0, 1, 0, 0}, // line 4
{0, 0, 0, 0, 0, 1, 0}, // line 5
{0, 0, 0, 0, 0, 0, 1} // line 6
}
};
return EYE;
}
Matrix Zero() { // 单位矩阵
static const Matrix ZERO = {6, 1,
{
{}, // line 0
{0, 0}, // line 1
{0, 0}, // line 2
{0, 0}, // line 3
{0, 0}, // line 4
{0, 0}, // line 5
{0, 0} // line 6
}
};
return ZERO;
}
Matrix Move(double a, double b) { // 平移 x+=a, y+=b
Matrix tmp = {
6, 6, {
{}, // line 0
{0, 1, 0, 0, 2*a, 0, a*a}, // line 1: x^2
{0, 0, 1, 0, 0, 2*b, b*b}, // line 2: y^2
{0, 0, 0, 1, b, a, a*b}, // line 3: xy
{0, 0, 0, 0, 1, 0, a}, // line 4: x
{0, 0, 0, 0, 0, 1, b}, // line 5: y
{0, 0, 0, 0, 0, 0, 1} // line 6: 1
}
};
return tmp;
}
Matrix _Rotate(double theta) { // 绕原点逆时针旋转 theta 弧度
double s = sin(theta), s2 = s*s;
double c = cos(theta), c2 = c*c, sc2 = 2*s*c, sc=s*c;
Matrix tmp = {
6, 6, {
{}, // line 0
{0, c2, s2, -sc2, 0, 0, 0}, // line 1: x^2
{0, s2, c2, +sc2, 0, 0, 0}, // line 2: y^2
{0, sc, -sc, c2-s2, 0, 0, 0}, // line 3: xy
{0, 0, 0, 0, c, -s, 0}, // line 4: x
{0, 0, 0, 0, s, c, 0}, // line 5: y
{0, 0, 0, 0, 0, 0, 1} // line 6: 1
}
};
return tmp;
}
Matrix _Flip() { // 沿着 x 轴对称 : y -> -y
Matrix tmp = {6, 6,
{
{}, // line 0
{0, 1, 0, 0, 0, 0, 0}, // line 1: x^2
{0, 0, 1, 0, 0, 0, 0}, // line 2: y^2
{0, 0, 0,-1, 0, 0, 0}, // line 3: xy
{0, 0, 0, 0, 1, 0, 0}, // line 4: x
{0, 0, 0, 0, 0,-1, 0}, // line 5: y
{0, 0, 0, 0, 0, 0, 1} // line 6: 1
}
};
return tmp;
}
Matrix _Project() { // 投影到 x 轴上: y -> 0
Matrix tmp = {6, 6,
{
{}, // line 0
{0, 1, 0, 0, 0, 0, 0}, // line 1: x^2
{0, 0, 0, 0, 0, 0, 0}, // line 2: y^2
{0, 0, 0, 0, 0, 0, 0}, // line 3: xy
{0, 0, 0, 0, 1, 0, 0}, // line 4: x
{0, 0, 0, 0, 0, 0, 0}, // line 5: y
{0, 0, 0, 0, 0, 0, 1} // line 6: 1
}
};
return tmp;
}
Matrix _Resize(double lambda) { // 以原点为中心缩放
double l = fabs(lambda), l2 = l*l;
Matrix tmp = {6, 6,
{
{}, // line 0
{0, l2, 0, 0, 0, 0, 0}, // line 1: x^2
{0, 0, l2, 0, 0, 0, 0}, // line 2: y^2
{0, 0, 0, l2, 0, 0, 0}, // line 3: xy
{0, 0, 0, 0, l, 0, 0}, // line 4: x
{0, 0, 0, 0, 0, l, 0}, // line 5: y
{0, 0, 0, 0, 0, 0, 1} // line 6: 1
}
};
return tmp;
}
/* 以 a, b为中心旋转 */
Matrix Rotate(double a, double b, double theta) {
Matrix m1 = Move(-a, -b);
Matrix m2 = _Rotate(theta);
Matrix m3 = Move(a, b);
return (m3 * m2) * m1;
}
/* 以 a, b 为中心缩放 */
Matrix Resize(double a, double b, double lambda) {
Matrix m1 = Move(-a, -b);
Matrix m2 = _Resize(lambda);
Matrix m3 = Move(a, b);
return (m3 * m2) * m1;
}
/* 关于直线 y = tan(theta)x + y0 对称 */
Matrix Flip(double theta, double y0) {
Matrix m1 = Move(0, -y0);
Matrix m2 = _Rotate(-theta);
Matrix m3 = _Flip();
Matrix m4 = _Rotate(+theta);
Matrix m5 = Move(0, +y0);
return m5 * m4 * m3 * m2 * m1;
}
/* 投影到直线 y = tan(theta)x + y0*/
Matrix Project(double theta, double y0) {
Matrix m1 = Move(0, -y0);
Matrix m2 = _Rotate(-theta);
Matrix m3 = _Project();
Matrix m4 = _Rotate(+theta);
Matrix m5 = Move(0, +y0);
return m5 * m4 * m3 * m2 * m1;
}
}
const int maxn = (5e5 + 10); // 最大点数
double _X[maxn], _Y[maxn];
namespace SegT { // 维护区间和,允许区间乘一个数
const int maxm = maxn * 2; // 最大结点数
int lch[maxm], rch[maxm], lzy[maxm]; // lzy != 0 表示有 lzy 要被下传
Matrix LazyMat[maxm]; // lzay 矩阵
Matrix SumMat [maxm]; // 区间和矩阵
int ncnt = 0;
/* 维护修改 */
void maintain(int rt) {
SumMat[rt] = SumMat[lch[rt]] + SumMat[rch[rt]];
}
/* 标记下传 */
void pushdown(int rt) {
if(lzy[rt]) {
if(lch[rt]) {
int nl = lch[rt];
int nr = rch[rt];
/* 修改子节点 Sum */
SumMat[nl] = LazyMat[rt] * SumMat[nl];
SumMat[nr] = LazyMat[rt] * SumMat[nr];
/* 修改子节点 lazy */
LazyMat[nl] = LazyMat[rt] * LazyMat[nl];
LazyMat[nr] = LazyMat[rt] * LazyMat[nr];
/* 修改子节点 lazy 标记 */
lzy[nl] = 1;
lzy[nr] = 1;
}
LazyMat[rt] = MatrixGen::Eye(); // 单位矩阵
}
lzy[rt] = 0; // 清除标记
}
void build(int rt, int l, int r) {
if(l == r) {
SumMat [rt] = MatrixGen::Node(_X[l], _Y[l]);
LazyMat[rt] = MatrixGen::Eye();
}else {
int nl = ++ ncnt;
int nr = ++ ncnt;
int mid = (l + r) >> 1;
build(nl, l, mid);
build(nr, mid+1, r);
lch[rt] = nl;
rch[rt] = nr;
/* 修改该 LazyMat 和 SumMat */
LazyMat[rt] = MatrixGen::Eye();
maintain(rt);
}
}
/* 在结点上打标记 */
void tag(int rt, const Matrix& mat) {
if(rt) {
SumMat [rt] = mat * SumMat [rt];
LazyMat[rt] = mat * LazyMat[rt];
lzy[rt] = 1;
}
}
/* 区间乘法 */
void update(int rt, int l, int r, int ql, int qr, const Matrix& mat) {
if(r < ql || qr < l) return; // 没有交集
if(ql <= l && r <= qr) { // 被包含
tag(rt, mat); return;
}
pushdown(rt); // 标记下传
int mid = (l + r) >> 1;
update(lch[rt], l, mid, ql, qr, mat);
update(rch[rt], mid+1, r, ql, qr, mat);
/* 维护区间和 */
maintain(rt);
}
/* 计算区间和 */
Matrix query(int rt, int l, int r, int ql, int qr) {
if(r < ql || qr < l ) return MatrixGen::Zero(); // 没交集
if(ql <= l && r <= qr) return SumMat[rt]; // 被包含
pushdown(rt);
int mid = (l + r) >> 1;
Matrix ml = query(lch[rt], l, mid, ql, qr);
Matrix mr = query(rch[rt], mid+1, r, ql, qr);
/* 维护区间和 */
maintain(rt);
return ml + mr;
}
}
int main() {
//std::ios::sync_with_stdio(false);
int N, Q; std::cin >> N >> Q;
for(int i = 1; i <= N; i ++) {
//std::cin >> _X[i] >> _Y[i]; // 输入所有点的坐标
scanf("%lf%lf", &_X[i], &_Y[i]);
}
SegT::build(++ SegT::ncnt, 1, N);
for(int i = 1; i <= Q; i ++) { // 处理所有询问
//std::cerr << "[DEBUG] " << "i = " << i << std::endl;
//int op; std::cin >> op;
int op; scanf("%d", &op);
switch(op) {
case 1: { // 平移运动
//double ql, qr, a, b; std::cin >> ql >> qr >> a >> b;
double ql, qr, a, b; scanf("%lf%lf%lf%lf", &ql ,&qr, &a, &b);
SegT::update(1, 1, N, ql, qr, MatrixGen::Move(a, b));
break;
}
case 2: { // a, b, theta 旋转
//double ql, qr, a, b, theta; std::cin >> ql >> qr >> a >> b >> theta;
double ql, qr, a, b, theta; scanf("%lf%lf%lf%lf%lf", &ql, &qr, &a, &b, &theta);
SegT::update(1, 1, N, ql, qr, MatrixGen::Rotate(a, b, theta));
break;
}
case 3: { // a, b, lambda 缩放
//double ql, qr, a, b, lambda; std::cin >> ql >> qr >> a >> b >> lambda;
double ql, qr, a, b, lambda; scanf("%lf%lf%lf%lf%lf", &ql, &qr, &a, &b, &lambda);
SegT::update(1, 1, N, ql, qr, MatrixGen::Resize(a, b, lambda));
break;
}
case 4: { // theta, y0 直线轴对称
//double ql, qr, theta, y0; std::cin >> ql >> qr >> theta >> y0;
double ql, qr, theta, y0; scanf("%lf%lf%lf%lf", &ql, &qr, &theta, &y0);
SegT::update(1, 1, N, ql, qr, MatrixGen::Flip(theta, y0));
break;
}
case 5: { // theta, y0 直线投影
//double ql, qr, theta, y0; std::cin >> ql >> qr >> theta >> y0;
double ql, qr, theta, y0; scanf("%lf%lf%lf%lf", &ql, &qr, &theta, &y0);
SegT::update(1, 1, N, ql, qr, MatrixGen::Project(theta, y0));
break;
}
case 6: { // 查询区间重心
//double ql, qr; std::cin >> ql >> qr;
double ql, qr; scanf("%lf%lf", &ql, &qr);
Matrix tmp = SegT::query(1, 1, N, ql, qr);
int len = (qr - ql + 1);
//std::cout << tmp.A[4][1] / len << " " << tmp.A[5][1] / len << std::endl;
printf("%lf %lf\n", tmp.A[4][1] / len, tmp.A[5][1] / len);
break;
}
case 7: { // a, b 查询区间距离平方和
//double ql, qr, a, b; std::cin >> ql >> qr >> a >> b;
double ql, qr, a, b; scanf("%lf%lf%lf%lf", &ql, &qr, &a, &b);
SegT::update(1, 1, N, ql, qr, MatrixGen::Move(-a, -b));
Matrix tmp = SegT::query(1, 1, N, ql, qr);
SegT::update(1, 1, N, ql, qr, MatrixGen::Move(+a, +b));
//std::cout << tmp.A[1][1] + tmp.A[2][1] << std::endl;
printf("%lf\n", tmp.A[1][1] + tmp.A[2][1]);
break;
}
default:
assert(false /* 操作不合法 */ );
}
}
return 0;
}