// Arup Guha
// 12/16/2017
// Solution to 2013 SER Problem: It Takes a Village.

/*** Disclaimer
// This solution is very, heavily based on artur's judge solution for the problem.
***/

import java.util.*;
import java.io.*;

public class village {

	// Store graph here.
    public static int n;
    public static int e;
    public static HashSet[] eList;
	public static HashSet<Long> notB;

	// To find bridges
    public static int bridges;      // number of bridges
    public static int cnt;          // counter
    public static int[] pre;        // pre[v] = order in which dfs examines v
    public static int[] low;        // low[v] = lowest preorder of any vertex connected to v
	public static HashSet<Long> listB;	// I store my bridges here.

	// Store look up between component and low and high indexes for interval tree here.
	public static int[] compLow;
	public static int[] compHigh;
	public static int[] compNum;
	public static int preOrderIndex;

    public static void main(String[] args) throws Exception {

		BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
		StringTokenizer tok = new StringTokenizer(stdin.readLine());
		n = Integer.parseInt(tok.nextToken());
		e = Integer.parseInt(tok.nextToken());
		int numQ = Integer.parseInt(tok.nextToken());

		// Process each case.
		while (n != 0) {

			StringBuffer output = new StringBuffer();

			// Set up empty graph.
			eList = new HashSet[n];
			for (int i=0; i<n; i++) eList[i] = new HashSet<Integer>();

			notB = new HashSet<Long>();

			// Fill graph.
			for (int i=0; i<e; i++) {

				tok = new StringTokenizer(stdin.readLine());
				int v1 = Integer.parseInt(tok.nextToken()) - 1;
				int v2 = Integer.parseInt(tok.nextToken()) - 1;

				// Regular edge.
				if (!eList[v1].contains(v2)) {
					eList[v1].add(v2);
					eList[v2].add(v1);
				}

				// These are not bridges since they are doubly connected.
				else {
					notB.add( ((long)v1)*n+v2);
					notB.add( ((long)v2)*n+v1);
				}
			}

			// Find where the bridges are.
			runBridge();

			// Do the tree traversal (never forming the tree per se), labeling each component with a preorder labeling,
			// for starting and finishing order.
			labelTree();

			// Store the interval tree to do efficient updates required for the problem.
			IntTree itree = new IntTree(1,preOrderIndex);

			// Process queries.
			for (int i=0; i<numQ; i++) {
				tok = new StringTokenizer(stdin.readLine());
				String op = tok.nextToken();

				// Adding a trading post.
				if (op.equals("+")) {
					int v = Integer.parseInt(tok.nextToken()) - 1;
					int value = Integer.parseInt(tok.nextToken());
					itree.change(compLow[v], compHigh[v], value);
				}

				// Answering a query.
				else {
					int v = Integer.parseInt(tok.nextToken()) - 1;
					output.append(itree.query(compLow[v], compLow[v])+"\n");
				}
			}

			// Just output this whole case.
			System.out.print(output);

			// Get next case.
			tok = new StringTokenizer(stdin.readLine());
			n = Integer.parseInt(tok.nextToken());
			e = Integer.parseInt(tok.nextToken());
			numQ = Integer.parseInt(tok.nextToken());
		}
    }

    // Traverses underlying tree structure, labeling the preorder order of visitation of each component and when each
    // component is "finished."
    public static void labelTree() {
    	preOrderIndex = 1;
		boolean[] usedNode = new boolean[n];
		compLow = new int[n];
		compHigh = new int[n];
		compNum = new int[n];
		compLow[0] = 1;
		labelTreeRec(usedNode, 0, 0);

		// Update our high colors, since these might be off for some vertices,
		// since our graph isn't a tree. But at least one node in each component has it right.
		int[] newHigh = new int[preOrderIndex+1];
		for (int i=0; i<n; i++)
			newHigh[compLow[i]] = Math.max(newHigh[compLow[i]], compHigh[i]);
		for (int i=0; i<n; i++)
			compHigh[i] = newHigh[compLow[i]];
    }

    public static void labelTreeRec(boolean[] used, int from, int v) {

    	// Mark it - update preorder numbering.
    	used[v] = true;

    	if (!listB.contains( ((long)n)*from + v ))
    		compLow[v] = compLow[from];
    	else
    		compLow[v] = preOrderIndex;

   		// Recursively visit neighbors.
    	for (Integer item : (HashSet<Integer>)eList[v]) {
    		if (!used[item]) {

    			long eCode = ((long)n)*v + item;

    			// We only update our preOrder # if we are crossing a bridge.
    			if (listB.contains(eCode)) preOrderIndex++;
    			labelTreeRec(used, v, item);
    		}
    	}

    	// Mark end range.
   		compHigh[v] = preOrderIndex;
    }

	/*** The Bridge code is from Sedgewick ***/
	public static void runBridge() {
		listB = new HashSet<Long>();
        low = new int[n];
        pre = new int[n];
        for (int v = 0; v < n; v++)
            low[v] = -1;
        for (int v = 0; v < n; v++)
            pre[v] = -1;

        for (int v = 0; v < n; v++)
            if (pre[v] == -1 && eList[v].size() > 0)
                dfs(v, v);
    }

    public static void dfs(int u, int v) {

        pre[v] = cnt++;
        low[v] = pre[v];
        for (int w : (HashSet<Integer>)eList[v]) {

            if (pre[w] == -1) {
                dfs(v, w);
                low[v] = Math.min(low[v], low[w]);
                if (low[w] == pre[w] && !notB.contains(((long)v)*n+w)) {
                    listB.add( ((long)v)*n+w);
                    listB.add( ((long)w)*n+v);
                    bridges++;
                }
            }

            // update low number - ignore reverse of edge leading to v
            else if (w != u)
                low[v] = Math.min(low[v], pre[w]);
        }
    }
}

/*** My Interval Tree Code ***/
class IntTree {

	// Stores range for this node.
	public int low;
	public int high;

	// Stores aggregate data for this node.
	public int delta;
	public int value;

	// Pointers to children.
	public IntTree left;
	public IntTree right;

	// Creates an interval tree from myLow to myHigh, inclusive.
	public IntTree(int myLow, int myHigh) {

		low = myLow;
		high = myHigh;
		delta = 0;
		value = 0; /*** Can change ***/

		// Lowest level.
		if (low == high) {
			left = null;
			right = null;
		}

		// Must create more nodes.
		else {
			int mid = (low+high)/2;
			left = new IntTree(low, mid);
			right = new IntTree(mid+1, high);
		}
	}

	// Propogates a change down to child nodes.
	public void prop() {

		// Recursive case, push down.
		if (left != null) {
			left.delta += delta;	/*** can change ***/
			right.delta += delta;	/*** can change ***/
			delta = 0;
		}

		// I put this in, seems to make sense.
		else {
			value += delta; /*** Can change - set for sum now ***/
			delta = 0;
		}
	}

	// Pre-condition: delta is 0.
	public void update() {
		if (left != null)
			value = left.value+left.delta*(left.high-left.low+1)+right.value+right.delta*(right.high-right.low+1); /*** Can change ***/
	}

	// Changes the values in the range [start, end] by "adding" extra.
	public void change(int start, int end, int extra) {

		// Out of range.
		if (high < start || end < low) return;

		// Push down delta.
		prop();

		// Whole range must be updated.
		if (start <= low && high <= end) {
			delta += extra;		/*** Can change ***/
			update();
			return;
		}

		// Portions of children have to be changed instead.
		left.change(start, end, extra);
		right.change(start, end, extra);
		update();
	}

	public int query(int start, int end) {

		// Out of range.
		if (high < start || end < low) return  0; /*** Can change ***/

		// Whole range must be returned;
		if (start <= low && high <= end) {
			return value + delta*(high-low+1);
		}

		// Get answers from both potions.
		prop();
		int leftSide = left.query(start, end);
		int rightSide = right.query(start, end);
		update();
		return leftSide+rightSide;
	}
}