import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.StringTokenizer;
class HeavyLightNoRecursion {
segmentTree value[];
List<Integer>[] tree;
int[] size;
int[] parent;
int[] tin;
int[] tout;
int time;
int[] path;
int[] pathSize;
int[] pathPos;
int[] pathRoot;
int pathCount;
int len1;
int[][] up;
public HeavyLightNoRecursion(List<Integer>[] tree) {
this.tree = tree;
int n = tree.length;
size = new int[n];
parent = new int[n];
tin = new int[n];
tout = new int[n];
len1 = 1;
while ((1 << len1) <= n) ++len1;
up = new int[len1][n];
calcSizeParentTinTout(0);
path = new int[n];
pathSize = new int[n];
pathPos = new int[n];
pathRoot = new int[n];
buildPaths(0);
value=new segmentTree[pathCount];
for (int i = 0; i < pathCount; i++) {
int m = pathSize[i];
value[i]=new segmentTree(new long[m]);
}
}
class segmentTree
{
int n; // array size
long t[];
public segmentTree(long[] arr)
{
n=arr.length;
t=new long[3*n];
for(int i=0;i<n;i++)
{
t[n+i]=arr[i];
}
build();
}
void build() { // build the tree
for (int i
= n
- 1; i
> 0; --i
) t
[i
] = Math.
max(t
[i
<<1],t
[i
<<1|1]); }
void modify(int p, long value) {
for (t
[p
+= n
] = value
; p
> 1; p
>>= 1) t
[p
>>1] = Math.
max(t
[p
],t
[p
^1]); }
long query(int l, int r) { // sum on interval [l, r)
long res = 0;
for (l += n, r += n; l < r; l >>= 1, r >>= 1) {
if ((l
&1)!=0) res
=Math.
max(res , t
[l
++]); if ((r
&1)!=0) res
= Math.
max(res, t
[--r
]); }
return res;
}
}
int query_up(int u, int v) {
if(u == v) return 0;
int uchain, vchain = path[v], ans = -1;
while(true) {
uchain = path[u];
if(uchain == vchain) {
if(u==v) break;
ans
=(int) Math.
max(value
[path
[u
]].
query( pathPos
[v
]+1,pathPos
[u
]+1),ans
);
break;
}
ans
=(int) Math.
max(value
[path
[u
]].
query(0,pathPos
[u
]+1),ans
);
u = pathRoot[uchain]; // move u to u's chainHead
u = parent[u]; //Then move to its parent, that means we changed chains
}
return ans;
}
int query(int u, int v) {
/*
* We have a query from u to v, we break it into two queries, u to LCA(u,v) and LCA(u,v) to v
*/
int lca = lca(u, v);
int ans = query_up(u, lca); // One part of path
int temp = query_up(v, lca); // another part of path
if(temp > ans) ans = temp; // take the maximum of both paths
return ans;
}
void calcSizeParentTinTout(int root) {
int n = tree.length;
int[] curEdge = new int[n];
int[] stack = new int[n];
stack[0] = root;
parent[root] = root;
for (int top = 0; top >= 0; ) {
int u = stack[top];
if (curEdge[u] == 0) {
//from
up[0][u] = parent[u];
for (int i = 1; i < len1; i++)
up[i][u] = up[i - 1][up[i - 1][u]];
//to
tin[u] = time++;
size[u] = 1;
}
if (curEdge[u] < tree[u].size()) {
int v = tree[u].get(curEdge[u]++);
if (curEdge[v] == 0) {
stack[++top] = v;
parent[v] = u;
}
} else {
--top;
if (parent[u] != u)
size[parent[u]] += size[u];
tout[u] = time++;
}
}
parent[root]=-1;
}
int newPath(int u) {
pathRoot[pathCount] = u;
return pathCount++;
}
void buildPaths(int root) {
int n = tree.length;
int[] curEdge = new int[n];
int[] stackPath = new int[n];
int[] stack = new int[n];
stack[0] = root;
stackPath[0] = newPath(root);
for (int top = 0; top >= 0; ) {
int u = stack[top];
int path = stackPath[top];
if (curEdge[u] == 0) {
this.path[u] = path;
pathPos[u] = pathSize[path]++;
}
if (curEdge[u] < tree[u].size()) {
int v = tree[u].get(curEdge[u]++);
if (curEdge[v] == 0) {
stack[++top] = v;
stackPath[top] = 2 * size[v] >= size[u] ? path : newPath(v);
}
} else {
--top;
}
}
}
void buildPaths(int u, int path) {
this.path[u] = path;
pathPos[u] = pathSize[path]++;
for (int v : tree[u]) {
if (v != parent[u])
buildPaths(v, 2 * size[v] >= size[u] ? path : newPath(v));
}
}
boolean isAncestor(int p, int ch) {
return tin[p] <= tin[ch] && tout[ch] <= tout[p];
}
void change(int ver, int val) {
int pa=path[ver];
int pospa=pathPos[ver];
value[pa].modify(pospa, val);
}
public int lca(int a, int b) {
if (isAncestor(a, b))
return a;
if (isAncestor(b, a))
return b;
for (int i = len1 - 1; i >= 0; i--)
if (!isAncestor(up[i][a], b))
a = up[i][a];
return up[0][a];
}
public static void main
(String[] args
) {
FastScanner in
=new FastScanner
(System.
in); in.nextInt();
int n=in.nextInt();
Hashtable
<Long, Integer
> edgew
=new Hashtable
<>(); Hashtable
<Integer, Long
> edgeidx
=new Hashtable
<>(); for(int i=0;i<n;i++)
{
arr[i]=new ArrayList<Integer>();
}
for(int e=1;e<=(n-1);e++)
{
int a=in.nextInt()-1;
int b=in.nextInt()-1;
int c=in.nextInt();
long imge=edge(a,b);
edgew.put(imge,c);
edgeidx.put(e, imge);
arr[a].add(b);
arr[b].add(a);
}
HeavyLightNoRecursion hdl=new HeavyLightNoRecursion(arr);
for(int i=1;i<=n-1;i++)
{
long ab=edgeidx.get(i);
int a=(int)(ab>>32);
int b=(int)((ab<<32)>>32);
if(hdl.parent[a]==b)
{
hdl.change(a, edgew.get(ab));
}
else
{
hdl.change(b, edgew.get(ab));
}
}
l:
while(true)
{
switch(in.next())
{
case "QUERY":
int a1=in.nextInt()-1;
int b1=in.nextInt()-1;
out.println(hdl.query(a1,b1));
break;
case "CHANGE":
int i=in.nextInt();
int w=in.nextInt();
long ab=edgeidx.get(i);
int a=(int)(ab>>32);
int b=(int)((ab<<32)>>32);
edgew.put(ab, w);
if(hdl.parent[a]==b)
{
hdl.change(a, edgew.get(ab));
}
else
{
hdl.change(b, edgew.get(ab));
}
break;
default:
break l;
}
}
out.close();
}
static long edge(int u, int v) {
return ((long) Math.
min(u, v
) << 32) + Math.
max(u, v
); }
static class FastScanner {
public FastScanner
(File f
) { try {
e.printStackTrace();
}
}
}
while (st == null || !st.hasMoreTokens()) {
try {
s = br.readLine();
e.printStackTrace();
}
if (s == null)
return null;
}
return st.nextToken();
}
boolean hasMoreTokens() {
while (st == null || !st.hasMoreTokens()) {
try {
s = br.readLine();
e.printStackTrace();
}
if (s == null)
return false;
}
return true;
}
int nextInt() {
}
long nextLong() {
return Long.
parseLong(next
()); }
double nextDouble() {
return Double.
parseDouble(next
()); }
}
}