題意
現在有一棵在三維空間中的樹,樹上每條邊一定是某個坐標軸(x/y/z)正/反方向的向量。
現在有兩類操作。
旋轉操作:u v axis degree。(axis=x/y/z,degree=90/180/270。 表示將邊(u,v)以從u出發向axis的正方向向量為軸,旋轉degree度。)
詢問操作:u v。你需要回答u,v之間的歐式距離。
n,Q <= 1e5
題解
旋轉操作類型可以看作空間向量的繞軸旋轉(此處向量為父節點指向子節點的空間向量)
其中R就是三階的旋轉矩陣。繞X、Y、Z軸旋轉(右手螺旋規則)的變換矩陣為
建立根節點值為向量(0, 0, 0)其余結點值為父節點指向該節點的向量的有根樹,dfs序輕重鏈剖分該樹。用線段樹維護樹鏈的向量和,則維護了u相對於v的向量,該向量的模即為u、v間的歐幾里得距離,那么可以在時間復雜度完成操作2。用線段樹懶標記維護區間操作,從而實現子樹的乘法以及單點乘法,可以在
復雜度下完成操作1,注意操作1涉及兩種不同的操作,一類是“父親固定,旋轉兒子”,對應兒子所在子樹的區間乘,另一類是“兒子固定,父親旋轉”,對應兒子所在子樹的區間乘 + 兒子單點乘 + 整棵樹的區間乘。
具體實現如下。
(這場比賽拿了個金,還不錯。
代碼
#include <bits/stdc++.h>
#define IOS std::cin.tie(nullptr)->sync_with_stdio(false);
using namespace std;
using ll = __int128_t;
using FLOAT_ = long double;
template <typename T, int maxn>
class matrix {
private:
int n;
T a[maxn+1][maxn+1];
public:
explicit matrix(int n = 3) : n(n) { clear(); }
matrix(const initializer_list<initializer_list<T>>& lst) {
this->n = lst.size();
int i = 1;
for(auto it = lst.begin();it != lst.end();++it, ++i) {
int j = 1;
for(auto it2 = it->begin();it2 != it->end();++it2, ++j) {
a[i][j] = *it2;
}
}
}
void resize(int n) { this->n = n; clear(); }
void clear() {
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
a[i][j] = 0;
}
}
}
// res = m1 * m2
friend matrix operator*(const matrix& m1, const matrix& m2) {
assert(m1.n == m2.n);
int n = m1.n;
matrix res(n);
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
for(int k = 1;k <= n;++k) {
res.a[i][j] += m1.a[i][k] * m2.a[k][j];
}
}
}
return res;
}
matrix<T, maxn>& operator=(const matrix<T, maxn>& oth) {
n = oth.n;
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
a[i][j] = oth.a[i][j];
}
}
return *this;
}
matrix<T, maxn> operator+(const matrix<T, maxn>& oth) const {
assert(n == oth.n);
matrix<T, maxn> res(n);
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
res.a[i][j] = a[i][j] + oth.a[i][j];
}
}
return res;
}
matrix<T, maxn> operator-() const {
matrix<T, maxn> res(n);
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
res.a[i][j] = -a[i][j];
}
}
return res;
}
matrix<T, maxn> operator-(const matrix<T, maxn>& oth) const {
return (*this) + (-oth);
}
bool operator==(const matrix<T, maxn>& oth) const {
if(n != oth.n) { return false; }
for(int i = 1;i <= n;++i) {
for(int j = 1;j <= n;++j) {
if(a[i][j] != oth.a[i][j]) { return false; }
}
}
return true;
}
bool operator!=(const matrix<T, maxn>& oth) const {
return !(*this == oth);
}
int size() const { return n; }
T* operator[](int x) { return a[x]; }
const T* operator[](int x) const { return a[x]; }
T& X() { return a[1][1]; }
T& Y() { return a[2][1]; }
T& Z() { return a[3][1]; }
T X() const { return a[1][1]; }
T Y() const { return a[2][1]; }
T Z() const { return a[3][1]; }
FLOAT_ get_dis() const {
return sqrt((FLOAT_)(X() * X() + Y() * Y() + Z() * Z()));
}
string to_string() const {
string res = "matrix{\n";
for(int i = 1;i <= n;++i) {
res += "{";
for(int j = 1;j <= n;++j) {
res += std::to_string(a[i][j]);
if(j < n) {
res += ", ";
}
}
res += "}";
if(i < n) {
res += ", ";
}
res += "\n";
}
res += "}\n";
return res;
}
};
using Vector3 = matrix<ll, 3>;
const matrix<ll, 3> I = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1}
};
const matrix<ll, 3> rx90 = {
{1, 0, 0},
{0, 0, -1},
{0, 1, 0}
};
const matrix<ll, 3> rx180 = {
{1, 0, 0},
{0, -1, 0},
{0, 0, -1}
};
const matrix<ll, 3> rx270 = {
{1, 0, 0},
{0, 0, 1},
{0, -1, 0}
};
const matrix<ll, 3> ry90 = {
{0, 0, 1},
{0, 1, 0},
{-1, 0, 0}
};
const matrix<ll, 3> ry180 = {
{-1, 0, 0},
{0, 1, 0},
{0, 0, -1}
};
const matrix<ll, 3> ry270 = {
{0, 0, -1},
{0, 1, 0},
{1, 0, 0}
};
const matrix<ll, 3> rz90 = {
{0, -1, 0},
{1, 0, 0},
{0, 0, 1}
};
const matrix<ll, 3> rz180 = {
{-1, 0, 0},
{0, -1, 0},
{0, 0, 1}
};
const matrix<ll, 3> rz270 = {
{0, 1, 0},
{-1, 0, 0},
{0, 0, 1}
};
inline void getVector3(Vector3& dst, const string& axis, int w) {
dst.clear();
if(axis.back() == '-') { w = -w; }
if(axis.front() == 'x') {
dst.X() += w;
} else if(axis.front() == 'y') {
dst.Y() += w;
} else {
dst.Z() += w;
}
}
const int maxn = 1e5 + 50;
int n, q;
struct edge_node {
int to, nxt;
int w;
string axis;
} edge[maxn<<1];
int head[maxn], id = 2;
inline void add_edge(int u, int v, int w, const string& axis) {
edge[id] = edge_node {v, head[u], w, axis};
head[u] = id++;
string axis_r = axis.back() == '+' ? axis.front() + string("-") : axis.front() + string("+");
edge[id] = edge_node {u, head[v], w, axis_r};
head[v] = id++;
}
Vector3 wi[maxn];
int dfn[maxn], cnt = 0, depth[maxn], sz[maxn], top[maxn], fa[maxn], son[maxn];
Vector3 dfnw[maxn];
void dfs1(int u, int pre) {
depth[u] = depth[pre] + 1;
sz[u] = 1;
fa[u] = pre;
for(int i = head[u]; i;i = edge[i].nxt) {
int v = edge[i].to;
if(v != pre) {
getVector3(wi[v], edge[i].axis, edge[i].w);
dfs1(v, u);
sz[u] += sz[v];
if(sz[son[u]] < sz[v]) {
son[u] = v;
}
}
}
}
void dfs2(int u, int t) {
dfn[u] = ++cnt;
dfnw[cnt] = wi[u];
top[u] = t;
if(!son[u]) return;
dfs2(son[u], t);
for(int i = head[u]; i;i = edge[i].nxt) {
int v = edge[i].to;
if(v != fa[u] && v != son[u]) {
dfs2(v, v);
}
}
}
#define lson (rt<<1)
#define rson (rt<<1|1)
#define mid (l+((r-l)>>1))
struct ty {
Vector3 sum;
Vector3 mul;
} tree[maxn<<2];
inline void push_up(int rt) {
auto& res = tree[rt];
auto& lres = tree[lson];
auto& rres = tree[rson];
res.sum = lres.sum + rres.sum;
}
void build(int rt, int l, int r) {
tree[rt].mul = I;
if(l == r) {
tree[rt].sum = dfnw[l];
return;
}
build(lson, l, mid);
build(rson, mid + 1, r);
push_up(rt);
}
inline void push_mul(int rt, const Vector3& mul) {
tree[rt].sum = mul * tree[rt].sum;
tree[rt].mul = mul * tree[rt].mul;
}
inline void push_down(int rt) {
if(tree[rt].mul != I) {
const auto& mul = tree[rt].mul;
push_mul(lson, mul);
push_mul(rson, mul);
tree[rt].mul = I;
}
}
void mul(int rt, int l, int r, int x, int y, const Vector3& val) {
if(x == l && r == y) {
push_mul(rt, val);
return;
}
push_down(rt);
if(y <= mid) {
mul(lson, l, mid, x, y, val);
} else if(x > mid) {
mul(rson, mid + 1, r, x, y, val);
} else {
mul(lson, l, mid, x, mid, val);
mul(rson, mid + 1, r, mid + 1, y, val);
}
push_up(rt);
}
Vector3 query(int rt, int l, int r, int x, int y) {
if(x == l && r == y) {
return tree[rt].sum;
}
push_down(rt);
if(y <= mid) {
return query(lson, l, mid, x, y);
} else if(x > mid) {
return query(rson, mid + 1, r, x, y);
}
auto lres = query(lson, l, mid, x, mid);
auto rres = query(rson, mid + 1, r, mid + 1, y);
return lres + rres;
}
#undef lson
#undef rson
#undef mid
void mul_tree(int u, const Vector3& val) {
mul(1, 1, n, dfn[u], dfn[u] + sz[u] - 1, val);
}
void mul_vertex(int u, const Vector3& val) {
mul(1, 1, n, dfn[u], dfn[u], val);
}
FLOAT_ query_path(int u, int v) {
Vector3 udis, vdis, dis;
while(top[u] != top[v]) {
if(depth[top[u]] >= depth[top[v]]) {
udis = udis + query(1, 1, n, dfn[top[u]], dfn[u]);
u = fa[top[u]];
} else {
vdis = vdis + query(1, 1, n, dfn[top[v]], dfn[v]);
v = fa[top[v]];
}
}
if(depth[u] >= depth[v]) {
udis = udis + query(1, 1, n, dfn[v], dfn[u]);
auto lca_dis = query(1, 1, n, dfn[v], dfn[v]);
dis = udis - lca_dis - vdis;
} else {
vdis = vdis + query(1, 1, n, dfn[u], dfn[v]);
auto lca_dis = query(1, 1, n, dfn[u], dfn[u]);
dis = vdis - lca_dis - udis;
}
return dis.get_dis();
}
signed main() {
IOS
cin >> n >> q;
for(int i = 1;i <= n - 1;++i) {
int u, v, w;
string axis;
cin >> u >> v >> w >> axis;
add_edge(u, v, w, axis);
}
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
while(q--) {
int op;
cin >> op;
if(op == 1) {
int u, v;
char axis;
int deg;
cin >> u >> v >> axis >> deg;
if(axis == 'x') {
if(fa[v] == u) {
if(deg == 90) {
mul_tree(v, rx90);
} else if(deg == 180) {
mul_tree(v, rx180);
} else {
mul_tree(v, rx270);
}
} else {
if(deg == 90) {
mul_tree(u, rx270);
mul_vertex(u, rx90);
mul_tree(1, rx90);
} else if(deg == 180) {
mul_tree(u, rx180);
mul_vertex(u, rx180);
mul_tree(1, rx180);
} else {
mul_tree(u, rx90);
mul_vertex(u, rx270);
mul_tree(1, rx270);
}
}
}
if(axis == 'y') {
if(fa[v] == u) {
if(deg == 90) {
mul_tree(v, ry90);
} else if(deg == 180) {
mul_tree(v, ry180);
} else {
mul_tree(v, ry270);
}
} else {
if(deg == 90) {
mul_tree(u, ry270);
mul_vertex(u, ry90);
mul_tree(1, ry90);
} else if(deg == 180) {
mul_tree(u, ry180);
mul_vertex(u, ry180);
mul_tree(1, ry180);
} else {
mul_tree(u, ry90);
mul_vertex(u, ry270);
mul_tree(1, ry270);
}
}
}
if(axis == 'z') {
if(fa[v] == u) {
if(deg == 90) {
mul_tree(v, rz90);
} else if(deg == 180) {
mul_tree(v, rz180);
} else {
mul_tree(v, rz270);
}
} else {
if(deg == 90) {
mul_tree(u, rz270);
mul_vertex(u, rz90);
mul_tree(1, rz90);
} else if(deg == 180) {
mul_tree(u, rz180);
mul_vertex(u, rz180);
mul_tree(1, rz180);
} else {
mul_tree(u, rz90);
mul_vertex(u, rz270);
mul_tree(1, rz270);
}
}
}
} else {
int u, v;
cin >> u >> v;
auto res = query_path(u, v);
cout << fixed << setprecision(15) << res << '\n';
}
}
return 0;
}