【HDU5923 2016CCPC东北地区大学生程序设计竞赛 - 重现赛 B】【并查集 暴力 复杂度计算】Prediction 生效若干条链上的所有边条件下的联通块情况

Prediction

Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 356    Accepted Submission(s): 63


Problem Description
There is a graph   G =\left \langle V_G, E_G \right \rangle   with   |V_G| = n  and   |E_G| = m, and a magic tree   T =\left \langle V_T, E_T \right \rangle)  rooted at 1, which contains m vertices.

Each vertex of the magic tree corresponds to an edge in the original graph G and each edge occurs in the magic tree exactly once.

Each query includes a set   S(S \subseteq V_T), and you should tell Mr. Frog the number of components in the modified graph   G^` = (V_G, E_G^`), where   E_G^`  is a set of edges in which every edge corresponds to a vertex v in magic tree T satisfying at least one of the following two conditions:

\bullet v \in S.
\bullet v is an ancestor of some vertices in S.

Note that the queries are independent, and namely one query will not influence another.
 

Input
The input contains several test cases and the first line of the input data is an integer T, denoting the number of test cases.

For each test case, the first line contains two integers n and m( 1 \leq n \leq 500,1 \leq m \leq 10000), where n is the number of vertices and m is the number of edges.

The second line contains m - 1 integers describing the magic tree, i-th integer represents the parent of the (i + 1)-th vertex.

Then the following m lines describe the edges of the graph G. Each line contains two integers u and v indicating the two ends of the edge.

The next line contains only one integer q( 1 \leq q \leq 50000), indicating the number of queries.

Then the following q lines represent queries,  i-th line represents the i-th query, which contains an integer   k_i  followed by   k_i  integers representing the set   S_i.

It is guarenteed that   \sum_{i=1}^{q}k_i \leq 300000 .
 

Output
For each case, print a line "Case #x:", where x is the case number (starting from 1).

For each query, output a single line containing only one integer representing the answer, namely the number of components.
 

Sample Input
  
  
1 5 4 1 1 3 1 2 2 3 3 4 4 5 3 1 2 2 2 3 2 2 4
 

Sample Output
  
  
Case #1: 3 2 1
Hint
magic tree and the original graph in the sample are: In the first query, S = {2} and the modified graph G' = {{1, 2, 3, 4}, {(1, 2), (2, 3)}}, thus the number of the components in the modified graph is 3. In the second query, S = {1, 2, 3}, where 1 is the ancestor of 2 (and 3) in the magic tree, and the modified graph G'' = {{1, 2, 3,4}, {(1, 2), (2, 3), (3, 4)}}, therefore the number of the components in the modified graph is 2. In the third query, S = {1, 2, 3, 4}, where 1 is the ancestor of 2 (and 4), 3 is the ancestor of 4, and the modified graph G' = {{1, 2, 3,4}, {(1, 2), (2, 3), (3,4), (4, 5)}}, therefore the answer equals to 1.
 

Source

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = 505, M = 1e4 + 10, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n, m;
vector<int>a[M];
pair<int, int>b[M];
int f[M][N];
inline int find(int f[], int x)
{
	return f[x] == x ? x : f[x] = find(f, f[x]);
}
inline void Union(int f[], int x, int y)
{
	x = find(f, x);
	y = find(f, y);
	f[y] = x;
}
void dfs(int x, int fa)
{
	MC(f[x], f[fa]);
	Union(f[x], b[x].first, b[x].second);
	for (auto y : a[x])dfs(y, x);
}
int d[N];
void solve()
{
	printf("Case #%d:\n", casei);
	int q; scanf("%d", &q);
	while (q--)
	{
		MC(d, f[0]);
		int k, x; scanf("%d", &k);
		while (k--)
		{
			scanf("%d", &x);
			for (int i = 1; i <= n; ++i)
			{
				Union(d, i, find(f[x], i));
			}
		}
		int ans = 0;
		for (int i = 1; i <= n; ++i)if (find(d, i) == i)++ans;
		printf("%d\n", ans);
	}
}
int main()
{
	//fre();
	scanf("%d", &casenum);
	for (casei = 1; casei <= casenum; ++casei)
	{
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= m; ++i)a[i].clear();
		for (int i = 2; i <= m; ++i)
		{
			int x; scanf("%d", &x);
			a[x].push_back(i);
		}
		for (int i = 1; i <= m; ++i)scanf("%d%d", &b[i].first, &b[i].second);
		for (int i = 1; i <= n; ++i)f[0][i] = i;
		dfs(1, 0);
		solve();
	}
	return 0;
}
/*
【trick&&吐槽】
1,一定要跟榜,好好读题
2,看题有时不如看数据和hint更好理解
3,要通关观察数据研究复杂度

【题意】
这题比较难读——

给你一个图G,n(500)个点m(10000)条边
然后又一棵点数为m的树T,根为1,
树T的每个点对应为图的每条边
我们有q(50000)个询问
对于第i个询问,我们会给你一个点数为ki的集合Si
让你对于G'=(n个点,特定边集E)内有多少个联通块。

对于我们给定的集合Si
其所有点,以及所有点的所有祖先,都在这个集合中。

【类型】
语文题 并查集 复杂度计算

【分析】
首先分析题目的性质——
1,图G的点数很少
2,nm也才不过5e6
3,询问数为q=5e4,树T的点数m=1e4,
	意味着我们不能在每次询问之后就整理一下我们选取图n上的哪些边

对于树T,如果我们如果选择它,便会一同选择其所有祖先,也就是使得我们我们在图G上做一系列的联通操作
所以我们dfs树T,每个节点维护——如果生效从根到这个节点的所有点,会使得图G上的集合连通性变为怎样
这个维护的复杂度为n*m

然后对于每个询问,我们直接把取出所有点的并查集结果做合并就好了,复杂度为nk

【时间复杂度&&优化】
O(nm+nk)

这道题可以通过以下技巧优化——
1,并查集采取回溯型写法(可以使用启发式合并)
2,每个点不存集合关系而存边
3,处理询问的时候不用并查集,而用加边bfs的方式实现

【数据】
input
1
5 4
1 1 3
1 2
2 3
3 4
4 5
3
1 2
2 2 3
2 2 4

ans
Case #1:
3
2
1

5个点4条边的图G
4个点4-1条边的树T

树T长这个样子
1--3--4
|
2

图G长这个样子
1-2-3-4-5
 1 2 3 4

询问有3个
1 2
我们会保留图G上的边1和边2,也就使得联通块数为3
2 2 3
我们会保留图G上的边1边2和边3,也就使得联通块数为2
2 2 4
我们会保留图G上的边1边2边3和边4,也就是的联通块数为1


*/


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值