#include<bits/stdc++.h> using namespace std; #define REP(i,a,b) for(i=a;i<b;i++) #define rep(i,n) REP(i,0,n) #define ll long long #define ull unsigned ll #define mygc(c) (c)=getchar_unlocked() #define mypc(c) putchar_unlocked(c) void reader(int *x){int k,m=0;*x=0;for(;;){mygc(k);if(k=='-'){m=1;break;}if('0'<=k&&k<='9'){*x=k-'0';break;}}for(;;){mygc(k);if(k<'0'||k>'9')break;*x=(*x)*10+k-'0';}if(m)(*x)=-(*x);} void reader(int *x, int *y){reader(x);reader(y);} void reader(int *x, int *y, int *z){reader(x);reader(y);reader(z);} void writer(int x, char c){int sz=0,m=0;char buf[10];if(x<0)m=1,x=-x;while(x)buf[sz++]=x%10,x/=10;if(!sz)buf[sz++]=0;if(m)mypc('-');while(sz--)mypc(buf[sz]+'0');mypc(c);} template<class T> struct ullHash{ ull *key; T *val; unsigned memory, size, mask; void clear(){memset(val,0,size*sizeof(T));} void clear(int sz){size=1;while(size<sz)size*=2;mask=size-1;clear();} void init(int mem, int sz){memory=mem;size=1;while(size<sz)size*=2;mask=size-1;if(memory<size)memory=size;key=(ull*)malloc(memory*sizeof(ull));val=(T*)malloc(memory*sizeof(T));if(size)clear();} int function(ull a){return (a*97531)&mask;} T get(ull a){int i=function(a);for(;key[i]!=a&&val[i]!=0;)i=(i+1)&mask;if(key[i]!=a) return 0;return val[i];} void set(ull a, T v){int i=function(a);for(;key[i]!=a&&val[i]!=0;)i=(i+1)&mask;key[i]=a;val[i]=v;} T increase(ull a, T v = 1){int i=function(a);for(;key[i]!=a&&val[i]!=0;)i=(i+1)&mask;key[i]=a;return val[i]+=v;} }; template<class T> struct segtreeMinVal{ int size, mem; T *val, *minVal; void malloc(int N){mem=N;val=(T*)std::malloc(sizeof(T)*N);minVal=(T*)std::malloc(sizeof(T)*N*4);} void free(void){mem=0;std::free(val);std::free(minVal);} void init_sub(int node, int left, int right){int sz=right-left,n1=node*2+1,n2=node*2+2;if(sz==1)minVal[node]=val[left];else {init_sub(n1,left,left+sz/2);init_sub(n2,left+sz/2,right);minVal[node]=min(minVal[n1], minVal[n2]);}} void init(int N, int zerofill = 0){int i;size=N;if(zerofill)rep(i,N)val[i]=0;init_sub(0,0,N);} void change_sub(int node, int left, int right, int n){int sz=right-left,n1=node*2+1,n2=node*2+2;if(n<left||n>=right)return;if(sz==1)minVal[node]=val[left];else {change_sub(n1,left,left+sz/2,n);change_sub(n2,left+sz/2,right,n);minVal[node]=min(minVal[n1],minVal[n2]);}} void change(int n, T v){if(val[n]==v)return;val[n]=v;change_sub(0,0,size,n);} T get_minVal_sub(int node, int left, int right, int a, int b){int sz=right-left,n1=node*2+1,n2=node*2+2;if(a<left)a=left;if(b>right)b=right;if(a>=b)return numeric_limits<T>::max();if(a==left&&b==right)return minVal[node];return min(get_minVal_sub(n1,left,left+sz/2,a,b), get_minVal_sub(n2,left+sz/2,right,a,b));} T get_minVal(int a, int b){return get_minVal_sub(0,0,size,a,b);} }; #define INF 2000000001 int N, M, Q; int U[210000], V[210000], C[210000]; int TYPE, S, T, X, NEW_CAPACITY; ullHash<int> node2edge; ullHash<int> treeNode2edge; int edgeind_edgeU[210000], edgeind_edgeV[210000]; vector<int> edge[110000], cap[110000]; int cycleSize; vector<int> cycle[110000]; int node2cycle[110000], node2cycle_ind[110000]; int route[110000], visited[110000]; void find_cycle(int node, int depth, int bef){ int i, j, k; visited[node] = depth; route[depth] = node; rep(i,edge[node].size()){ k = edge[node][i]; if(k==bef) continue; if(visited[k] >= 0){ if(visited[k] <= depth){ cycle[cycleSize].clear(); REP(j,visited[k],depth+1) cycle[cycleSize].push_back(route[j]); cycleSize++; } } else { find_cycle(k, depth+1, node); } } } int treeN; int node2treeNode[110000], cycle2treeNode[110000], treeNode2cycle[110000]; int treeNodeType[110000], treeNodeInd[110000]; // TYPE : 0 = ordinary node, 1 = cycle vector<int> treeEdge[110000]; int blockSize; int treeNode2Block[110000], treeNode2Ind[110000]; int upBlock[110000], upInd[110000]; vector<int> block2node[110000]; vector<int> downBlock[110000], downFrom[110000]; int blockDepth[110000]; int subtreeSize[110000]; void getTreeSizeDfs(int node, int bef){ int i, k; subtreeSize[node] = 1; rep(i,treeEdge[node].size()){ k = treeEdge[node][i]; if(k==bef) continue; getTreeSizeDfs(k, node); subtreeSize[node] += subtreeSize[k]; } } void genBlock(int node, int bef, int bef_ind){ int i, k, mx, mx_ind; int block = blockSize++; if(bef >= 0){ upBlock[block] = treeNode2Block[bef]; upInd[block] = bef_ind; blockDepth[block] = blockDepth[upBlock[block]] + 1; } else{ blockDepth[block] = 0; } downBlock[block].clear(); downFrom[block].clear(); block2node[block].clear(); block2node[block].push_back(node); treeNode2Block[node] = block; treeNode2Ind[node] = 0; for(;;){ mx = mx_ind = -1; rep(i,treeEdge[node].size()){ k = treeEdge[node][i]; if(k==bef) continue; if(mx < subtreeSize[k]){ mx = subtreeSize[k]; mx_ind = i; } } if(mx==-1) break; rep(i,treeEdge[node].size()){ if(i==mx_ind) continue; k = treeEdge[node][i]; if(k==bef) continue; downBlock[block].push_back(blockSize); downFrom[block].push_back(block2node[block].size()-1); genBlock(k, node, block2node[block].size()-1); } bef = node; node = treeEdge[node][mx_ind]; block2node[block].push_back(node); treeNode2Block[node] = block; treeNode2Ind[node] = block2node[block].size()-1; } } int topNodeOfBlock[110000], upNodeOfBlock[110000]; vector<int> topNodeOfTreeNode[110000], bottomNodeOfTreeNode[110000]; int edgeType[210000]; // 0 = in cycle, 1 = heavy edge, 2 = light edge int edge2cycle[210000], edge2cycle_ind[210000]; int edge2block[210000], edge2block_ind[210000]; segtreeMinVal<int> segCycle[110000], segBlock[110000]; int solveCycle(int a, int b){ if(a==b || node2cycle[a]==-1) return INF; assert( node2cycle[a] != -1 && node2cycle[a] == node2cycle[b] ); int res = 0; int c = node2cycle[a]; a = node2cycle_ind[a]; b = node2cycle_ind[b]; if(a > b) swap(a, b); res += segCycle[c].get_minVal(a,b); res += min(segCycle[c].get_minVal(0,a), segCycle[c].get_minVal(b,cycle[c].size())); return res; } int solveBlock(int a, int b){ int res = INF; assert(treeNode2Block[node2treeNode[a]] == treeNode2Block[node2treeNode[b]]); int bl = treeNode2Block[node2treeNode[a]]; if(treeNode2Ind[node2treeNode[a]] == treeNode2Ind[node2treeNode[b]]) return solveCycle(a, b); if(treeNode2Ind[node2treeNode[a]] > treeNode2Ind[node2treeNode[b]]) swap(a,b); int ai = treeNode2Ind[node2treeNode[a]], bi = treeNode2Ind[node2treeNode[b]]; res = min(res, solveCycle(a, bottomNodeOfTreeNode[bl][ai])); res = min(res, solveCycle(b, topNodeOfTreeNode[bl][bi])); res = min(res, segBlock[bl].get_minVal(ai*2+2, bi*2+1)); return res; } int solve(int a, int b){ int res = INF; int bl, ai; for(;;){ if(treeNode2Block[node2treeNode[a]] == treeNode2Block[node2treeNode[b]]){ res = min(res, solveBlock(a, b)); break; } if(blockDepth[treeNode2Block[node2treeNode[a]]] < blockDepth[treeNode2Block[node2treeNode[b]]]) swap(a, b); bl = treeNode2Block[node2treeNode[a]]; ai = treeNode2Ind[node2treeNode[a]]; res = min(res, solveCycle(a, topNodeOfTreeNode[bl][ai]) ); res = min(res, segBlock[bl].get_minVal(0, ai*2+1)); a = upNodeOfBlock[treeNode2Block[node2treeNode[a]]]; } return res; } void modify(int e, int v){ if(edgeType[e]){ segBlock[edge2block[e]].change(edge2block_ind[e], v); } else { int tn, bl, ind; segCycle[edge2cycle[e]].change(edge2cycle_ind[e], v); tn = cycle2treeNode[edge2cycle[e]]; bl = treeNode2Block[tn]; ind = treeNode2Ind[tn]; if(ind < block2node[bl].size()-1){ segBlock[bl].change(ind*2+1, solveCycle(topNodeOfTreeNode[bl][ind], bottomNodeOfTreeNode[bl][ind])); } } } int main(){ int i, j, k, e, x, y, z; int res; reader(&N,&M); rep(i,M){ reader(U+i,V+i,C+i), U[i]--, V[i]--; assert(0 <= U[i] && U[i] < N); assert(0 <= V[i] && V[i] < N); assert(1 <= C[i] && C[i] <= 1000000000); } node2edge.init(3*M+10, 3*M+10); treeNode2edge.init(3*M+10, 3*M+10); rep(i,M){ node2edge.set(U[i]*N+V[i], i+1); node2edge.set(V[i]*N+U[i], i+1); } rep(i,N) edge[i].clear(), cap[i].clear(); rep(i,M){ edgeind_edgeU[i] = edge[U[i]].size(); edgeind_edgeV[i] = edge[V[i]].size(); edge[U[i]].push_back(V[i]); cap[U[i]].push_back(C[i]); edge[V[i]].push_back(U[i]); cap[V[i]].push_back(C[i]); } rep(i,N) node2cycle[i] = -1; rep(i,N) visited[i] = -1; cycleSize = 0; find_cycle(0, 0, -1); rep(i,cycleSize){ rep(j,cycle[i].size()){ node2cycle[cycle[i][j]] = i; node2cycle_ind[cycle[i][j]] = j; } } treeN = 0; rep(i,N){ if(node2cycle[i]==-1 || node2cycle_ind[i]==0){ treeNode2cycle[treeN] = node2cycle[i]; if(node2cycle[i]==-1) node2treeNode[i] = treeN; else cycle2treeNode[node2cycle[i]] = treeN; if(node2cycle[i]==-1) treeNodeType[treeN] = 0, treeNodeInd[treeN] = i; else treeNodeType[treeN] = 1, treeNodeInd[treeN] = node2cycle[i]; treeN++; } } rep(i,N) if(node2cycle[i] >= 0) node2treeNode[i] = cycle2treeNode[node2cycle[i]]; rep(i,M){ if(node2cycle[U[i]] != -1 && node2cycle[U[i]]==node2cycle[V[i]]) continue; if(node2cycle[U[i]] == -1) x = node2treeNode[U[i]]; else x = cycle2treeNode[node2cycle[U[i]]]; if(node2cycle[V[i]] == -1) y = node2treeNode[V[i]]; else y = cycle2treeNode[node2cycle[V[i]]]; treeEdge[x].push_back(y); treeEdge[y].push_back(x); treeNode2edge.set(x*N+y, i+1); treeNode2edge.set(y*N+x, i+1); } blockSize = 0; getTreeSizeDfs(0, -1); genBlock(0, -1, -1); REP(k,1,blockSize){ x = block2node[k][0]; z = block2node[upBlock[k]][upInd[k]]; e = treeNode2edge.get(x*N+z) - 1; assert("ASSERT 1" && e >= 0); edgeType[e] = 2; edge2block[e] = k; edge2block_ind[e] = 0; if(node2treeNode[U[e]] == x) topNodeOfBlock[k] = U[e], upNodeOfBlock[k] = V[e]; else topNodeOfBlock[k] = V[e], upNodeOfBlock[k] = U[e]; } rep(k,blockSize){ topNodeOfTreeNode[k].clear(); bottomNodeOfTreeNode[k].clear(); topNodeOfTreeNode[k].push_back(topNodeOfBlock[k]); REP(i,1,block2node[k].size()){ x = block2node[k][i-1]; y = block2node[k][i]; e = treeNode2edge.get(x*N+y) - 1; assert("ASSERT 2" && e >= 0); edgeType[e] = 1; edge2block[e] = k; edge2block_ind[e] = 2*i; if(node2treeNode[U[e]] == x) topNodeOfTreeNode[k].push_back(V[e]), bottomNodeOfTreeNode[k].push_back(U[e]); else topNodeOfTreeNode[k].push_back(U[e]), bottomNodeOfTreeNode[k].push_back(V[e]); } } rep(k,cycleSize){ segCycle[k].malloc(cycle[k].size()); rep(i,cycle[k].size()){ e = node2edge.get(cycle[k][i]*N + cycle[k][(i+1)%cycle[k].size()]) - 1; edgeType[e] = 0; edge2cycle[e] = k; edge2cycle_ind[e] = i; segCycle[k].val[i] = C[e]; } segCycle[k].init(cycle[k].size()); } rep(k,blockSize){ segBlock[k].malloc(2*block2node[k].size()-1); rep(i,block2node[k].size()){ x = block2node[k][i]; if(i){ z = block2node[k][i-1]; segBlock[k].val[2*i] = C[ treeNode2edge.get(x*N+z)-1 ]; } else if(k==0){ segBlock[k].val[2*i] = INF; } else { z = block2node[upBlock[k]][upInd[k]]; segBlock[k].val[2*i] = C[ treeNode2edge.get(x*N+z)-1 ]; } if(i+1 < block2node[k].size()){ if(treeNodeType[x] == 0){ segBlock[k].val[2*i+1] = INF; } else if(k==0 && i==0) { segBlock[k].val[2*i+1] = INF; } else { segBlock[k].val[2*i+1] = solveCycle(topNodeOfTreeNode[k][i], bottomNodeOfTreeNode[k][i]); } } } segBlock[k].init(2*block2node[k].size()-1); } reader(&Q); while(Q--){ reader(&TYPE); if(TYPE==0){ reader(&S,&T); S--; T--; res = solve(S,T); writer(res, '\n'); } else { reader(&X,&NEW_CAPACITY); X--; modify(X, NEW_CAPACITY); } } return 0; }