/*
题意:有n个点,现在需要联通所有,有q种套餐可以选择,
当然套餐之外也可以自己添加边,意为达到最短距离。
题意很明显,不知道需要使用哪一种套餐,
那么需要枚举每一种套餐的情况。
然后再进行对比。
注意最开始没有套餐的情况。
*/
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = ;
class Cor
{
public:
int x,y;
}cor[maxn]; //坐标
class Node
{
public:
int x,y;
int dis;
}edge[maxn*maxn]; //边
vector<int>t[];
int cost[]; //每中套餐的花费
int n,m,q;
int f[maxn];
bool cmp(Node a,Node b)
{
return a.dis < b.dis;
}
double distances(Cor a,Cor b)
{
int dx = a.x - b.x;
int dy = a.y - b.y;
return (dx*dx + dy*dy);
}
void inti(int n)
{
for(int i = ;i <= n; i++)
f[i] = i;
return ;
}
int Find(int x)
{
int r = x;
while(r != f[r])
r = f[r];
int i = x,j;
while(f[i] != r){
j = f[i];
f[i] = r;
i = j;
}
return r;
}
int merge(int x,int y)
{
int a = Find(x);
int b = Find(y);
if(a == b)
return false;
else
f[a] = b;
return true;
}
int kruskal() //标准的kruskal
{
int pos = ,sum = ;
for(int i = ;i < m && pos < n - ; i++){
if(merge(edge[i].x,edge[i].y)){
pos++;
sum += edge[i].dis;
}
}
return sum;
}
void solve()
{
inti(n);
int ans = kruskal(); //初始的情况
for(int i = ;i < (<<q); i++){
int money = ;
inti(n);
for(int j = ;j < q; j++){ //枚举每一种套餐的情况
// if((i>>j)&1) continue;
if(i&(<<j)){
money += cost[j];
for(int k = ;k < t[j].size(); k++){
merge(t[j][k],t[j][]); //把套餐的点相连
}
}
}
ans = min(ans,kruskal() + money);
}
printf("%d\n",ans);
return ;
}
int main()
{
// freopen("in.txt","r",stdin);
int ncase,pn = ;
scanf("%d",&ncase);
while(ncase--){
if(pn > ) //UVA的输出
printf("\n");
pn++;
for(int i = ;i < ; i++) //要清理,初始化
t[i].clear();
scanf("%d%d",&n,&q);
for(int i = ;i < q; i++){ //vector是把每一种套餐记录,
// 因为不知道每一种套餐有几个点,所以用vector很合适
int num,temp;
scanf("%d%d",&num,&cost[i]);
for(int j = ;j < num; j++){
scanf("%d",&temp);
t[i].push_back(temp);
}
}
for(int i = ;i <= n; i++)
scanf("%d%d",&cor[i].x,&cor[i].y);
m = ;
for(int i = ;i <= n; i++){
for(int j = i + ;j <= n; j++){
edge[m].x = i;
edge[m].y = j;
edge[m++].dis = distances(cor[i],cor[j]);//计算每一条边的长度
}
}
sort(edge,edge+m,cmp); //kruskal特有
solve();
}
return ;
}