1.题目描述:点击打开链接
2.解题思路:本题利用拓扑排序+并查集解决。由于以前没用过用队列来模拟一个拓扑排序过程,比赛时候这道题做的异常艰难才通过==。如果用队列来处理的话,就会方便很多,首先入队列的是degree小于2的点,然后从这些点出发,和他相邻的点的degree都要减小1,如果发现相邻结点的degree也小于2了,那么加入队列,同时标记所有degre小于2的点,表示他们已经被删除了。这样,剩下的没有被标记的点一定是构成了一个环,然后用并查集来更新连通块的个数,权值,最后求和即可。
3.代码:
#include<iostream>
#include<algorithm>
#include<cassert>
#include<string>
#include<sstream>
#include<set>
#include<bitset>
#include<vector>
#include<stack>
#include<map>
#include<queue>
#include<deque>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
#include<cctype>
#include<complex>
#include<functional>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define me(s) memset(s,0,sizeof(s))
#define rep(i,n) for(int i=0;i<(n);i++)
typedef long long ll;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair <int, int> P;
const int N=10000+5;
struct Edge
{
int u,v;
}e[100000+10];
vector<int>g[N];
int p[N],c[N];
ll w[N];
int deg[N],vis[N];
int n,m;
void init()
{
me(g);me(vis);me(deg);
for(int i=0;i<n;i++)
p[i]=i,c[i]=1;
}
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}
void addedge(int id,int u,int v)
{
g[u].push_back(v);
g[v].push_back(u);
deg[u]++,deg[v]++;
e[id].u=u,e[id].v=v;
}
void toposort()//利用一个队列来模拟一个拓扑排序
{
queue<int>q;
me(vis);
for(int i=0;i<n;i++)
if(deg[i]<2)
{
q.push(i);
vis[i]=1;
}
while(!q.empty())
{
int u=q.front();q.pop();
int len=g[u].size();
for(int i=0;i<len;i++)
{
int v=g[u][i];
if(vis[v])continue;
deg[v]--;
if(deg[v]<2)
{
vis[v]=1;
q.push(v);
}
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
for(int i=0;i<n;i++)
scanf("%I64d",&w[i]);
int u,v;
for(int i=0;i<m;i++)
{
scanf("%d%d",&u,&v);
u--,v--;
addedge(i,u,v);
}
toposort();
for(int i=0;i<m;i++)
{
int u=e[i].u,v=e[i].v;
if(vis[u]||vis[v])continue;
int x=find(u),y=find(v);
if(x!=y)
{
c[y]+=c[x];
w[y]+=w[x];
p[x]=y;
}
}
ll ans=0;
for(int i=0;i<n;i++)
if(!vis[i]&&p[i]==i&&(c[i]&1))
ans+=w[i];
printf("%I64d\n",ans);
}
}