题意:有一个n个点m条边的图,有q次操作,操作1删掉一条a b之间的边,操作2询问a  b之间的必要边,必要边指的是,从a到b必须要经过的边。(题目说明了:在任何情况下,保证整个图的连通)

思路:

1、如果要直接计算图中两点联通的必要边的话,显然不太可行

2、那我们把完成所有操作后的图看成一棵树,和几条边,那么对应的操作就变成了加边和询问

3、树上任意两点保证有且只有一条路径,并且如果对于树询问必要边的话,就是路径上的边数

4、对于操作1,我们给树上两点加上一条路径,就意味着这两个点和他们路径上的点,这些点之间的任意两点可以通过两条路到达,即没有必要边,所以对于操作1,我们只需要将两点之间的路径的权值全部改为 0 就可以。

5、对于操作2,我们只需要查询一下两点在树上的距离就可以。

6、整合一下,整个题目就变成了,先求出最后的图,并且将最后的图变成一棵树加上若干条边,对于若干条边,用操作1将这些边的两个端点之间的距离设为0,反向询问,对于操作1,将这两个点之间的距离设为0,对于操作2,查询这两点在树上的距离,

7、所以大概就是     并查集+线段树+树链剖分(边权) https://blog.csdn.net/qq_41608020/article/details/89766333

8、对于将一个图变成一棵树和若干条边我们可以用并查集来操作

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lson left,mid,k<<1
#define rson mid+1,right,k<<1|1
#define imid int mid=(left+right)/2;
const ll MAXN = 100005;
struct edge
{
	int to;
	int nex;
}e[MAXN * 2];
int head[MAXN], tot;
int n, m, q;
int fa[MAXN], son[MAXN], deep[MAXN], num[MAXN];
int top[MAXN], p[MAXN], fp[MAXN];
int pos;
int ql, qr;
ll val;
void init()
{
	tot = 0;
	memset(head, -1, sizeof(head));
	pos = 1;
	memset(son, -1, sizeof(son));
}
void add(int a, int b)
{
	e[tot] = edge{ b,head[a] };
	head[a] = tot++;
}
void dfs1(int u, int pre, int dep)
{
	deep[u] = dep;
	fa[u] = pre;
	num[u] = 1;
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int v = e[i].to;
		if (v != pre)
		{
			dfs1(v, u, dep + 1);
			num[u] += num[v];
			if (son[u] == -1 || num[son[u]] < num[v])
				son[u] = v;
		}
	}
}
void dfs2(int u, int sp)
{
	top[u] = sp;
	p[u] = pos++;
	fp[p[u]] = u;
	if (son[u] == -1)
		return;
	dfs2(son[u], sp);
	for (int i = head[u]; i + 1; i = e[i].nex)
	{
		int v = e[i].to;
		if (v != son[u] && v != fa[u])
			dfs2(v, v);
	}
}

struct node
{
	int l;
	int r;
	ll sum;
	int mark;
}que[MAXN * 4];
void up(int k)
{
	que[k].sum = que[k << 1].sum + que[k << 1 | 1].sum;
}
void down(int k)
{
	if (que[k].mark)
	{
		que[k << 1].mark = que[k].mark;
		que[k << 1 | 1].mark = que[k].mark;
		que[k << 1].sum = 0;
		que[k << 1 | 1].sum = 0;
		que[k].mark = 0;
	}
}
void build(int left = 1, int right = pos, int k = 1)
{
	que[k].l = left;
	que[k].r = right;
	que[k].mark = 0;
	if (left == right)
		return;
	imid;
	build(lson);
	build(rson);
}
void update(int left = 1, int right = pos, int k = 1)
{
	if (qr < left || right < ql)
		return;
	if (ql <= left && right <= qr)
	{
		if (val == 0)
		{
			que[k].mark = 1;
			que[k].sum = 0;
		}
		else
		{
			que[k].sum = 1;
		}
		return;
	}
	down(k);
	imid;
	update(lson);
	update(rson);
	up(k);
}
ll query(int left = 1, int right = pos, int k = 1)
{
	if (qr < left || right < ql)
		return 0;
	if (ql <= left && right <= qr)
		return que[k].sum;
	down(k);
	imid;
	return query(lson) + query(rson);
}

void change(int u, int v)
{
	int f1 = top[u], f2 = top[v];
	while (f1 != f2)
	{
		if (deep[f1] < deep[f2])
		{
			swap(f1, f2);
			swap(u, v);
		}
		ql = p[f1];
		qr = p[u];
		val = 0;
		update();
		u = fa[f1];
		f1 = top[u];
	}
	if (u == v)
		return;
	if (deep[u] > deep[v])
		swap(u, v);
	ql = p[son[u]];
	qr = p[v];
	val = 0;
	update();
}

ll changes(int u, int v)
{
	ll res = 0;
	int f1 = top[u], f2 = top[v];
	while (f1 != f2)
	{
		if (deep[f1] < deep[f2])
		{
			swap(f1, f2);
			swap(u, v);
		}
		ql = p[f1];
		qr = p[u];
		res += query();
		u = fa[f1];
		f1 = top[u];
	}
	if (u == v)
		return res;
	if (deep[u] > deep[v])
		swap(u, v);
	ql = p[son[u]];
	qr = p[v];
	res += query();
	return res;
}

#define Pair pair<int,int>
int in[MAXN][3];
int op[MAXN][3];
int ques[MAXN];
int preop[MAXN][2];
int qq;

void initbcj()
{
	qq = 0;
	for (int i = 1; i <= n; i++)
		ques[i] = i;
}
int getf(int k)
{
	return ques[k] == k ? k : ques[k] = getf(ques[k]);
}
void merge(int a, int b)
{
	ques[getf(a)] = getf(b);
}

int main()
{
	int T, cas = 1;
	scanf("%d", &T);
	while (T--)
	{
		map<Pair, int>mp;
		vector<ll>ans;
		scanf("%d%d%d", &n, &m, &q);
		init();
		initbcj();
		for (int i = 0; i < m; i++)
		{
			scanf("%d%d", &in[i][0], &in[i][1]);
			if (in[i][0] > in[i][1])
				swap(in[i][0], in[i][1]);
			mp[Pair{ in[i][0],in[i][1] }]++;
			//add(in[i][0], in[i][1]);
			//add(in[i][1], in[i][0]);
		}
		for (int i = 0; i < q; i++)
		{
			scanf("%d%d%d", &op[i][0], &op[i][1], &op[i][2]);
			if (op[i][1] > op[i][2])
				swap(op[i][1], op[i][2]);
			if (op[i][0] == 1)
				mp[Pair{ op[i][1],op[i][2] }]--;
		}

		//重新做边
		m = 0;
		for (auto it = mp.begin(); it != mp.end(); it++)
		{
			if (it->second != 0)
			{
				int q = getf(it->first.first), w = getf(it->first.second);
				if (q != w)//树
				{
					add(it->first.first, it->first.second);
					add(it->first.second, it->first.first);
					in[m][0] = it->first.first;
					in[m][1] = it->first.second;
					in[m][2] = it->second;
					m++;
					merge(it->first.first, it->first.second);
				}
				else//若干条边
				{
					preop[qq][0] = it->first.first;
					preop[qq][1] = it->first.second;
					qq++;
				}
			}
		}

		dfs1(1, 0, 0);
		dfs2(1, 1);
		build();
		for (int i = 0; i < m; i++)
		{
			if (deep[in[i][0]] > deep[in[i][1]])
				swap(in[i][0], in[i][1]);
			ql = p[in[i][1]];
			qr = ql;
			val = in[i][2];
            //如果是重边和自环就没有必要边
			if (val > 1 || in[i][0] == in[i][1])
				val = 0;
			update();
		}

		//preop set 0   若干条边
		for (int i = 0; i < qq; i++)
			change(preop[i][0], preop[i][1]);

		for (int i = q - 1; i >= 0; i--)
		{
			if (op[i][0] == 1)
			{
				ql = op[i][1];
				qr = op[i][2];
				change(ql, qr);
			}
			else
			{
				ql = op[i][1];
				qr = op[i][2];
				ll res = changes(ql, qr);
				ans.push_back(res);
			}
		}
		printf("Case #%d:\n", cas++);
		int len = ans.size();
		for (int i = len - 1; i >= 0; i--)
		{
			printf("%lld\n", ans[i]);
		}
	}
}
/*
1
5 6 5
1 2
1 4
2 4
2 3
4 5
2 4
2 2 4
2 1 4
1 1 2
2 2 4
2 1 2

*/