#include <stdio.h>
#include <string.h>
#include <vector>
#include <map>
using namespace std;
#define NMAX 501
#define KMAX 10
#define MMAX 501
#define MAX_STATES 4150
#define INF 666666666
struct tree_node {
vector<int> *nodes;
map<int, int> *isInside;
int nstates;
int nupnodes;
int nupstates;
char state[MAX_STATES][KMAX];
int smin[MAX_STATES];
char upstate[MAX_STATES][KMAX];
int sextra[MAX_STATES];
map<int, int> *sextraMap;
};
struct tree_node tnode[NMAX];
vector<int> nodes[NMAX], edge[NMAX];
map<int, int> isInside[NMAX], sextraMap[NMAX];
int A[MMAX][MMAX];
char Asellocal[MMAX][MMAX];
char s[KMAX], used[KMAX], viz[NMAX], maxcols;
//int state[KMAX][MAX_STATES][KMAX];
//int smins[KMAX][MAX_STATES][KMAX];
int i, j, k, p, q, N, M, P, localmax;
void readInputData(void)
{
freopen("tproc.in", "r", stdin);
scanf("%d %d %d", &N, &M, &maxcols);
for (i = 1; i <= N; i++)
edge[i].clear();
for (k = 1; k < N; k++)
{
scanf("%d %d", &i, &j);
edge[i].push_back(j);
edge[j].push_back(i);
}
for (k = 1; k <= N; k++)
{
tnode[k].nodes = &(nodes[k]);
tnode[k].nodes->clear();
tnode[k].isInside = &(isInside[k]);
tnode[k].isInside->clear();
tnode[k].sextraMap = &(sextraMap[k]);
tnode[k].sextraMap->clear();
scanf("%d", &p);
for (i = 1; i <= p; i++)
{
scanf("%d", &j);
tnode[k].nodes->push_back(j);
}
}
memset(A, 0, sizeof(A));
scanf("%d", &P);
for (k = 1; k <= P; k++)
{
scanf("%d %d %d", &i, &j, &q);
A[i][j] = A[j][i] = q;
}
}
void genStates(char niv, char cmax, int n)
{
char i;
if (niv >= tnode[n].nodes->size())
{
tnode[n].nstates++;
memcpy(tnode[n].state[tnode[n].nstates], s, sizeof(s));
tnode[n].smin[tnode[n].nstates] = 0;
for (j = 0; j < tnode[n].nodes->size(); j++)
for (k = j + 1; k < tnode[n].nodes->size(); k++)
if (s[j] == s[k])
{
p = (*tnode[n].nodes)[j];
q = (*tnode[n].nodes)[k];
tnode[n].smin[tnode[n].nstates] += A[p][q];
}
}
else
{
for (i = 1; i <= cmax + 1 && i <= maxcols; i++)
{
s[niv] = i;
if (i > cmax)
genStates(niv + 1, cmax + 1, n);
else
genStates(niv + 1, cmax, n);
}
}
}
char* normalize(char *s, int K)
{
char i, cnt = 0;
memset(used, 0, sizeof(used));
for (i = 0; i < K; i++)
{
if (!used[s[i]])
used[s[i]] = ++cnt;
s[i] = used[s[i]];
}
return s;
}
int hash(char *s, int K)
{
int i, h = 0;
for (i = 0; i < K; i++)
h = (h << 3) + (s[i] - 1);
return h;
}
inline int getsextra(int n, char* s)
{
int stnum = (*(tnode[n].sextraMap))[hash(s, tnode[n].nupnodes)];
return tnode[n].sextra[stnum];
}
// n = nodul din arbore
// t = parintele lui n (in arbore)
void computeCols(int n, int t)
{
int x, y, p, q, inp, inq, nextra, nextra2, cmax, eq, st, localmin;
viz[n] = 1;
// reordoneaza nodurile grafului din cadrul nodului curent al arborelui
// (primele vor fi nodurile care apar si in cadrul parintelui nodului curent)
for (x = 0; x < tnode[n].nodes->size(); x++)
for (y = x + 1; y < tnode[n].nodes->size(); y++)
{
p = (*tnode[n].nodes)[x];
q = (*tnode[n].nodes)[y];
if ((*tnode[t].isInside)[p])
inp = 1;
else
inp = 0;
if ((*tnode[t].isInside)[q])
inq = 1;
else
inq = 0;
if ((!inp && inq) || (inp == inq && p > q))
{
(*tnode[n].nodes)[x] = q;
(*tnode[n].nodes)[y] = p;
}
}
tnode[n].nupnodes = 0;
for (x = 0; x < tnode[n].nodes->size(); x++)
{
p = (*tnode[n].nodes)[x];
(*tnode[n].isInside)[p] = x + 1;
if ((*tnode[t].isInside)[p])
tnode[n].nupnodes++;
}
// genereaza toate colorarile posibile cu nodurile grafului din nodul curent al arborelui
tnode[n].nstates = 0;
genStates(0, 0, n);
localmin = INF;
for (x = 1; x <= tnode[n].nstates; x++)
{
tnode[n].sextra[x] = 0;
if (tnode[n].smin[x] < localmin)
localmin = tnode[n].smin[x],
st = x;
}
// selecteaza muchiile ce trebuie adunate in contul lui 'localmin'
for (x = 0; x < tnode[n].nodes->size(); x++)
for (y = x + 1; y < tnode[n].nodes->size(); y++)
if (tnode[n].state[st][x] == tnode[n].state[st][y])
{
p = (*tnode[n].nodes)[x];
q = (*tnode[n].nodes)[y];
Asellocal[p][q] = Asellocal[q][p] = 1;
}
// adauga la starile generate contributia fiilor nodului curent din arbore
for (x = 0; x < edge[n].size(); x++)
{
y = edge[n][x];
if (!viz[y])
{
computeCols(y, n);
for (st = 1; st <= tnode[n].nstates; st++)
if (tnode[n].smin[st] < INF)
{
memset(used, 0, sizeof(used));
for (p = 0; p < tnode[y].nupnodes; p++)
{
q = (*tnode[y].nodes)[p];
q = (*tnode[n].isInside)[q] - 1;
used[tnode[n].state[st][q]] = 1;
s[p] = tnode[n].state[st][q];
}
nextra = getsextra(y, normalize(s, tnode[y].nupnodes));
tnode[n].sextra[st] += nextra;
}
}
}
for (st = 1; st <= tnode[n].nstates; st++)
if (tnode[n].smin[st] < INF)
{
if (tnode[n].sextra[st] < INF)
tnode[n].smin[st] += tnode[n].sextra[st];
else
tnode[n].smin[st] = INF;
/*
if (tnode[n].sextra[st] > 0 && tnode[n].sextra[st] < INF)
printf("!!! nod=%d, st=%d, supl cols=%d\n", n, st, tnode[n].sextra[st]);
*/
tnode[n].sextra[st] = 0;
}
// pastreaza doar prefixele de 'nupnodes' noduri
tnode[n].nupstates = 0;
for (x = 1; x <= tnode[n].nstates; x++)
{
memcpy(s, tnode[n].state[x], sizeof(s));
nextra = tnode[n].smin[x];
if (tnode[n].smin[x] >= INF)
nextra = INF;
else
{
for (p = 0; p < tnode[n].nupnodes; p++)
for (q = p + 1; q < tnode[n].nupnodes; q++)
if (tnode[n].state[x][p] == tnode[n].state[x][q])
nextra -= A[(*tnode[n].nodes)[p]][(*tnode[n].nodes)[q]];
}
if (tnode[n].nupstates == 0)
{
tnode[n].nupstates = 1;
memcpy(tnode[n].upstate[1], s, sizeof(s));
tnode[n].sextra[1] = nextra;
}
else
{
for (eq = 1, y = 0; y < tnode[n].nupnodes; y++)
if (s[y] != tnode[n].upstate[tnode[n].nupstates][y])
{
eq = 0;
break;
}
if (eq)
{
if (nextra < tnode[n].sextra[tnode[n].nupstates])
tnode[n].sextra[tnode[n].nupstates] = nextra;
}
else
{
tnode[n].nupstates++;
memcpy(tnode[n].upstate[tnode[n].nupstates], s, sizeof(s));
tnode[n].sextra[tnode[n].nupstates] = nextra;
}
}
}
for (x = 1; x <= tnode[n].nupstates; x++)
(*(tnode[n].sextraMap))[hash(tnode[n].upstate[x], tnode[n].nupnodes)] = x;
// afisaza niste informatii pentru debug
/*
printf("### nod=%d [tata=%d]\n", n, t);
printf("nnodes=%d, nstates=%d\n", tnode[n].nodes->size(), tnode[n].nstates);
printf("localmin=%d\n", localmin);
printf("nupnodes=%d, nupstates=%d\n", tnode[n].nupnodes, tnode[n].nupstates);
printf("nodes:");
for (x = 0; x < tnode[n].nodes->size(); x++)
printf(" %d", (*(tnode[n].nodes))[x]);
printf("\n");
printf("|states|\n");
for (st = 1; st <= tnode[n].nstates; st++)
{
if (tnode[n].smins[st] >= INF)
continue;
printf("-> state=%d:", st);
for (p = 0; p < tnode[n].nodes->size(); p++)
printf(" %d", tnode[n].state[st][p]);
printf(" : smins=%d\n", tnode[n].smin[st]);
}
printf("|upstates|\n");
for (st = 1; st <= tnode[n].nupstates; st++)
{
if (tnode[n].sextra[st] >= INF)
continue;
printf("-> upstate=%d:", st);
for (p = 0; p < tnode[n].nupnodes; p++)
printf(" %d", tnode[n].upstate[st][p]);
printf(" : hash=%d, sextra=%d\n", hash(tnode[n].upstate[st], tnode[n].nupnodes), tnode[n].sextra[st]);
}
*/
}
void writeOutputData(void)
{
int smin;
localmax = 0;
for (i = 1; i < M; i++)
for (j = i + 1; j <= M; j++)
if (Asellocal[i][j])
localmax += A[i][j];
printf("* local_smin=%d\n", localmax);
smin = INF;
for (i = 1; i <= tnode[1].nstates; i++)
if (tnode[1].smin[i] < smin)
smin = tnode[1].smin[i];
printf("* global_smin=%d\n", smin);
freopen("tproc.out", "w", stdout);
printf("%d\n", smin);
}
int main()
{
readInputData();
localmax = 0;
memset(viz, 0, sizeof(viz));
memset(Asellocal, 0, sizeof(Asellocal));
computeCols(1, 1);
writeOutputData();
return 0;
}