Problem:
C : Shopping / 買い物

問題文

料理が得意な2D君は昼ごはんを作ろうとしている。料理には N 個の材料 a_{0}, a_{1}, … , a_{N−1} が全てが必要である。

さて、今2D君の家の冷蔵庫には一つも材料が入っていないので、スーパーに買いに行かなければならない。スーパーでは材料 a_{i} を値段 x_{i} 円で買うことができる。

2D君は魔法使いでもあり、 M 種類の魔法が使える。 i 番目の魔法を材料 s_{i} にかければ材料 t_{i} に、また逆に t_{i} にかければ s_{i} に変えることができる。さらに、一つの材料に対して複数の魔法を繰り返して使うことができる。例えば、 p から q に変える魔法と、 q から r に変える魔法の2つを使って、 p から r を得ることができる。

2D君は魔法の力を借りてなるべく安く材料を揃えることにした。料理を完成させるために2D君が買う必要のある材料の、値段の総和の最小値を求めよ。

入力

入力は以下の形式で与えられる。

N
a_{0} x_{0}
…
a_{N−1} x_{N−1}
M
s_{0} t_{0}
…
s_{M−1} t_{M−1}

制約

数値はすべて整数である

材料の名前はすべて 1 文字以上 10 文字以下のアルファベット小文字からなる

i ≠ j なら a_{i} ≠ a_{j}

1 \≤ x_{i} \≤ 1,000

1 \≤ N \≤ 5,000

0 \≤ M \≤ {\rm min}(N(N−1)/2, 1000)

s_{i} ≠ t_{i}

s_{i},t_{i} の組に重複はない

s_{i},t_{i} は a_{0},…,a_{N−1} に含まれる

出力

答えを1行で出力せよ。

サンプル

サンプル入力1

2
tako 2
yaki 1
1
tako yaki

魔法によって安い yaki を tako に変えることができるので、 yaki を 2 個買えばよい。

サンプル出力1

2

サンプル入力2

5
a 1
b 2
c 2
d 4
e 3
5
b a
a c
c d
e b
c b

サンプル出力2

5

下に示すように、全ての材料を a から変えてそろえることができる。

a : a そのまま

b : a -> c -> b

c : a -> c

d : a -> c -> d

e : a -> b -> e

Solution in C (judge verdict: Accepted)

// AOJ 2664: Shopping
// 2017.12.3 bal4u@uu

#include <stdio.h>
#include <string.h>

#define HASHSIZ 10007
typedef struct { char *s; int id, w; } HASH;
HASH hash[HASHSIZ+2], *hashend = hash + HASHSIZ;			// Hash table

void insert(char *s, int w, int id)
{
	HASH *tp = hash + (101 * *s + 103 * *(s+w-1) + 107 * w) % HASHSIZ;
	while (tp->s != NULL) {
		if (tp->w == w && memcmp(tp->s, s, w) == 0) return;
		if (++tp == hashend) tp = hash;
	}
	tp->s = s, tp->id = id, tp->w = w;
}

int lookup(char *s, int w)
{
	HASH *tp = hash + (101 * *s + 103 * *(s+w-1) + 107 * w) % HASHSIZ;
	while (tp->s != NULL) {
		if (tp->w == w && memcmp(tp->s, s, w) == 0) break;
		if (++tp == hashend) tp = hash;
	}
	return tp->id;
}

#define MAX 5002
/* UNION-FIND library */
int p[MAX], rank[MAX];

void make_set(int x) { p[x] = x, rank[x] = 0; }
void link(int x, int y) {
	if (rank[x] > rank[y]) p[y] = x;
	else { p[x] = y; if (rank[x] == rank[y]) rank[y] = rank[y] + 1;	}
}
int find_set(int x) { if (x != p[x]) p[x] = find_set(p[x]);	return p[x]; }
void union_set(int x, int y) { link(find_set(x), find_set(y)); }

int x[MAX];
char s[MAX][20];

char buf[30], *bp;
int getint()
{
	int n = 0;
	while (*bp >= '0') n = (n<<3) + (n<<1) + (*bp++ & 0xf);
	return n;
}

int main()
{
	int n, m, i, j, ans;
	char *q;

	fgets(bp=buf, 10, stdin), n = getint();
	for (i = 0; i < n; i++) p[i] = i;

	for (i = 0; i < n; i++) {
		fgets(bp=s[i], 20, stdin);
		q = bp; while (*bp >= 'a') bp++;
		*bp = 0, insert(q, bp-q, i);
		bp++, x[i] = getint();
	}

	fgets(bp=buf, 10, stdin), m = getint();
	while (m--) {
		fgets(bp=buf, 30, stdin);
		q = bp; while (*bp >= 'a') bp++;
		*bp = 0, i = lookup(q, bp-q);

		q = ++bp; while (*bp >= 'a') bp++;
		*bp = 0, j = lookup(q, bp-q);
		union_set(i, j);
	}

	for (i = 0; i < n; i++) {
		j = find_set(i);
		if (x[i] < x[j]) x[j] = x[i];
	}
	for (ans = 0, i = 0; i < n; i++) ans += x[p[i]];
	printf("%d\n", ans);
	return 0;
}