// Solutia cauta repetat LCA folosind RMQ asa cum e scris in articol:
// http://infoarena.ro/lowest-common-ancestor
//
// Apoi cum zice un post de pe blog, costul drumului intre noduri este
// gasit cumva ca in problema stramosi.
#include<cstdio>
#include<vector>
using namespace std;
int const maxn=32000; // numarul maxim de noduri
int const maxp=15; // 2^maxp > maxn
int const maxlun=2*(maxn-1)+1;
int N, M, P;
struct Ed { int to, ct; }; Ed E[maxn-1], Adj[2*(maxn-1)];
// Daca se face un subarbore copil al unei frunze, nodul
// din care s-a scos nu se mai repeta o data in Str, iar
// nodul in care s-a pus se repeta o data in plus. Astfel
// pentru orice arbore se foloseste un numar fix de pozitii
// in Str si anume 2*(N-1)+1
// Inaltimea intr-un arbore de segmente pentru un interval cu N
// elemente este H=[logN]+1. Pentru ca intervalele se injumatatesc
// de la parinte la copil. Astfel, numarul de noduri este maxim
// 2^0+2^1+...+2^H=2^{H+1}-1. Iese in mare mai putin de 5*N.
int
Str[maxlun] // o listare convenabila pentru a calcula lowest common ancestor
, Val[1+maxn] // adancimea folosita ca valoare in range minimum query ce da LCA-ul
, Pos[1+maxn] // o pozitie ca intrare pentru RMQ
, Lun // lungimea lui Str
, NC[1+maxn] // neighbour count
, NP[2+maxn] // neighbour pos
, NI[1+maxn] // neighbour index
, St[maxn][2] // stack folosit in calculul Str
, Min[1+(4*maxlun)] // folosite in implementarea arborelui de intervale folosit in RMQ
, A[maxn][maxp] // folosit ca in stramosi
, C[maxn][maxp] // extensie la stramosi
;
// Segment tree (arbore de intervale) : operatii update, query
// [ opereaza pe Min cu Val ]
// Se trece dintr-un interval intr-altul de lungime injumatatita pana
// cand se ajunge la un interval punct, asa ca T(n)=T(n/2)+O(1) ce da
// T(n)=O(log(N)) pentru un arbore pe intervalul 1..N
void update (int q, int st, int dr, int poz, int val) {
if ( (poz <= st) && (dr <= poz) ) {
if ( (-1 == Min[q]) || (Val[Min[q]]>Val[val]) ) { Min[q]=val; }
return;
}
int mij=(( st + dr ) >> 1), q1=(q << 1), q2=(1 + q1);
if (poz <= mij) { update (q1, st, mij, poz, val) ;
if ( (-1==Min[q]) || (Val[Min[q]]>Val[Min[q1]]) ) { Min[q]=Min[q1]; }
} else { update (q2,1+mij, dr, poz, val) ;
if ( (-1==Min[q]) || (Val[Min[q]]>Val[Min[q2]]) ) { Min[q]=Min[q2]; }
}
}
// Cand se intra intr-un interval stanga, tot timpul cazul interval dreapta
// se rezolva in O(1), similar pentru cand se intra intr-un interval dreapta.
// T(n)=T(n/2)+O(1) -> O(log(N)) pentru un arbore pe intervalul 0..N
int query (int q, int st, int dr, int a, int b) {
if ( (a<=st) && (dr<=b) ) {
return Min[q] ;
}
int mij=(( st + dr ) >> 1), q1=(q << 1), q2=(1+q1), x=-1, y=-1;
if (a <= mij) { x = query (q1, st, mij, a, b); }
if (b > mij) { y = query (q2 , mij+1, dr, a, b); }
if (-1==x) { return y;} if (-1==y) {return x;}
if (Val[x]>Val[y]) {return y;} return x;
}
// Calculeaza costul minim intre un nod i si
// un stramos r ; se foloseste adancimea Val
// pentru a se stii cate muchii sunt de urcat
// de la i pa na la r. Apoi se urca in mod
// binar folosindu-se C[][] pentru a lua
// minimul cat mai multor numere intre i si r
// si A[][] pentru a se schimba i-ul cand
// nu mai putem creste al doilea indice.
int
minCost (int i, int r) {
if ( i==r ) { return -1; }
int k = Val[i]-Val[r]; // numarul de muchii intre i si r
int s=1, miin=C[i][0], z=0;
while ( s<=k ) {
if ( miin>C[i][z] ) { miin=C[i][z]; }
if ( 2*s<k ) { ++z; s*=2; } else { i=A[i][z]; k-=s; z=0; s=1; }
}
return miin;
}
int main ( ) {
FILE *fi= fopen("atac.in","r"); fscanf(fi,"%d %d %d", &N, &M,&P);
int s, mi, re, X, Y, cA, cB, cC, cD, sp, el, in, lca, hd, tl, ct, po, a, b, el2;
for ( s=2; N>=s; ++s ) { fscanf(fi,"%d %d", &E[s-2].to, &E[s-2].ct); }
fscanf(fi, "%d %d %d %d %d %d", &X, &Y, &cA, &cB, &cC, &cD); fclose(fi);
// Scriu lista de adiacenta intr-un mod mai compact:
// Calculez numarul de vecini pentru fiecare nod, apoi numar numarul
// de vecini pana la
for ( s=1; N>=s; ++s ) { NC[s]=0; }
for ( s=2; N>=s; ++s ) { ++NC[s]; ++NC[E[s-2].to]; }
NP[1]=0; for ( s=2; N+1>=s; ++s ) { NP[s]=NP[s-1]+NC[s-1]; }
for ( s=1; N>=s; ++s ) { NI[s]=NP[s]; }
for ( s=2; N>=s; ++s) { tl=s; hd=E[s-2].to; ct=E[s-2].ct;
Adj[NI[tl]].to=hd; Adj[NI[tl]].ct=ct; ++NI[tl];
Adj[NI[hd]].to=tl; Adj[NI[hd]].ct=ct; ++NI[hd];
}
// Calculez Str, Val, Pos, Lun, A[i][0], C[i][0]
for ( s=1; N>=s; ++s ) { Pos[s]=-1; Val[s]=-1; }
St[0][0]=1; St[0][1]=NP[1]; A[1][0]=0; C[1][0]=0; sp=0; while (0<=sp) { el=St[sp][0]; in=St[sp][1];
if (-1 == Pos[el]) { Pos[el]=Lun; Val[el]=sp; } Str[Lun++]=el;
while ( (NP[1+el]>in) && (-1 != Pos[Adj[in].to]) ) { ++ in; }
if ( NP[1+el]>in ) { St[sp][1]=in; ++sp; el2=Adj[in].to; St[sp][0]=el2; St[sp][1]=NP[el2];
A[el2][0]=el; C[el2][0]=Adj[in].ct;
}
else { --sp; }
}
// Pregatesc arborele de intervale.
for ( s=1,re=4*Lun; re>=s; ++s ) { Min[s]=-1; }
for ( s=0; Lun>s; ++s ) { update(1, 0, Lun-1, s, Str[s]); }
// Calculez costurile ca la stramosi
// A[i][j] - stramos cu 2^j muchii pana la i
// A[i][j+1] = A[A[i][j]][j] pentru ca urcam 2^j muchii pana la A[i][j]
// si de acolo inca 2^j muchii
// C[i][j] - costul pe muchiile dintre i si A[i][j]
// [ costul il luam ca minimul costurilor muchiilor ]
// C[i][j+1] = min { C[i][j] , C[A[i][j]][j] }
mi=0; po=1; while ( 2*po<N ) { ++mi; po*=2;
for (s=1; N>=s; ++s) {
if (0 == A[s][mi-1]) {
A[s][mi]=0; C[s][mi]=-1;
} else {
if (0==A[A[s][mi-1]][mi-1]) {
A[s][mi]=0; C[s][mi]=-1;
} else {
A[s][mi]=A[A[s][mi-1]][mi-1];
C[s][mi]=C[A[s][mi-1]][mi-1]>C[s][mi-1]?C[s][mi-1]:C[A[s][mi-1]][mi-1];
}
}
}
}
// Misc X, Y folosind relatia data. La fiecare miscare se calculeaza un LCA
// in log(N) si 2 min cost, fiecare in log(N). Dau O(M*log(N)) operatii.
fi=fopen ("atac.out", "w");
for ( s=1; M>=s; ++s ) {
if (X==Y) {
if (s>M-P) { fprintf (fi, "0\n"); }
mi=0;
} else {
a=Pos[X]; b=Pos[Y]; if (a>b) { po=a; a=b; b=po; }
lca=query(1,0,Lun-1,a,b);
mi=minCost(X, lca); // cel putin unul din X si Y
re=minCost(Y, lca); // nu-i lca, asa ca mi si re nu-s
// in acelasi timp -1
if (-1==mi) { mi=re; } if (-1==re) { re=mi; }
if (mi>re) { mi=re; }
if ( s>M-P ) { fprintf(fi, "%d\n", mi); }
}
X=((X*cA +Y*cB)%N)+1;
Y=((Y*cC+mi*cD)%N)+1;
}
fclose(fi);
return 0;
}