Ponds HDU - 5438
原题链接
考察:拓扑排序+并查集
错误思路:
离线处理,\(d[i]\)记录i的入度.如果\(d[i]<=1\)就不纳入并查集,否则就加入.
错误原因:
删除一个点,可能使别的点\(d[i]<=1\)
思路:
因为\(d[i]\)是会级联影响的,所以我们用拓扑排序求\(d[i]<=1\)的点.但是注意题目是无向边,我们要特判环防止TLE.
Code
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 10010,M = 100010;
struct Road{
int to,ne;
}road[M<<1];
int n,m,d[N],idx,h[N],q[N],p[N],L[N],R[N];
LL w[N];
bool vis[N];
int sz[N];
void add(int a,int b)
{
road[idx].to =b,road[idx].ne = h[a],h[a] = idx++;
}
int findf(int x)
{
if(p[x]!=x) return p[x] = findf(p[x]);
return p[x];
}
void merge(int a,int b)
{
int pa = findf(a),pb = findf(b);
if(pa==pb) return;
sz[pb]+=sz[pa];
w[pb]+=w[pa];
p[pa] = pb;
}
void topsort()
{
int hh = 0,tt=-1;
for(int i=1;i<=n;i++)
if(d[i]<=1) q[++tt] = i;
while(hh<=tt)
{
int u = q[hh++];
vis[u] = 1;
for(int i=h[u];~i;i=road[i].ne)
{
int v = road[i].to;
if(vis[v]) continue;
if(--d[v]<=1) q[++tt] = v;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(h,-1,sizeof h);
memset(d,0,sizeof d);
memset(vis,0,sizeof vis);
scanf("%d%d",&n,&m);
idx =0;
for(int i=1;i<=n;i++) scanf("%lld",&w[i]),p[i] = i,sz[i] =1;
for(int i=1;i<=m;i++)
{
int a,b;
scanf("%d%d",&a,&b);
d[a]++,d[b]++;
add(a,b); add(b,a);
L[i] = a,R[i] = b;
}
topsort();
for(int i=1;i<=m;i++)
{
if(vis[L[i]]||vis[R[i]]) continue;
merge(L[i],R[i]);
}
LL res = 0;
for(int i=1;i<=n;i++)
{
int pa = findf(i);
if(vis[pa]) continue;
vis[pa] = 1;
if(sz[pa]&1) res+=w[pa];
}
printf("%lld\n",res);
}
return 0;
}