题目链接:[HDU 5438]Ponds[并查集]
题意分析:
不断删去度数为1的点,判断最终元素个数为奇数个的联通块里面的点的权值之和。
解题思路:
用并查集确定联通块,然后用dfs删点,最终O(n)跑一遍统计联通块内点的个数,顺便记录当中的和,就OK了。
个人感受:
赛后再做,发现代码竟然一样= =。不愧是自己写的码。不过这回没初始化一个数组,WA 3。2333
具体代码如下:
#include<iostream>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
const int MAXN = 1e4 + 111;
ll val[MAXN];
ll sum[MAXN];
int cnt[MAXN], p[MAXN], ran[MAXN], e[MAXN]; // cnt:联通块内元素个数 e:点度数
vector<int> v[MAXN];
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}
void unite(int a, int b)
{
a = find(a), b = find(b);
if (a == b) return;
if (ran[a] > ran[b]) p[b] = a;
else
{
p[a] = b;
if (ran[a] == ran[b]) ++ran[b];
}
}
void dfs(int x)
{
--e[x];
for (int j = 0; j < v[x].size(); ++j)
{
int cur = v[x][j];
--e[cur];
if (e[cur] == 1)
dfs(cur);
}
}
int main()
{
int t, n, m, a, b; scanf("%d", &t);
while (t --)
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
{
scanf("%lld", &val[i]);
p[i] = i;
sum[i] = cnt[i] = ran[i] = e[i] = 0;
v[i].clear();
}
for (int i = 0; i < m; ++i)
{
scanf("%d%d", &a, &b);
unite(a, b);
++e[a], ++e[b];
v[a].push_back(b);
v[b].push_back(a);
}
for (int i = 1; i <= n; ++i) if (e[i] == 1) dfs(i);
for (int i = 1; i <= n; ++i) // 统计联通块内元素个数
{
if (e[i] > 1)
{
int x = find(i);
++cnt[x];
sum[x] += val[i];
}
}
ll ans = 0;
for (int i = 1; i <= n; ++i)
{
if (cnt[i] % 2)
{
ans += sum[i];
}
}
printf("%lld\n", ans);
}
return 0;
}