题意:给出一张无向图,每个节点有各自的权值,问在点数为奇数的圈中的点的权值总和是多少。
通过拓扑序的做法标记出所有非圈上的点,做法就是加每条边的时候将两点的入度都加一,然后将所有度数为1的点入队,删去它的所有边,即对没条边连的点度数减一,度数减为1继续入队,直到队列为空,入过队列的点都进行标记,表示该点不在圈上,那么剩余没有标记的点一定要么在圈上、要么是单点或自环。这时候只要对于每个没有访问过的节点,DFS遍历它的连通区域,记录点数和权值和,如果点数为奇数,则统计权值和。但是注意如果只有一个点,不能算作一个圈,所以除了判断点数是否奇数,还需要判断点数大于1。
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<iostream>
typedef long long ll;
using namespace std; const int maxn=;
const int maxm=; int head[maxn],point[maxm],nxt[maxm],id[maxn],size,vis[maxn];
int v[maxn];
int n,m; void init(){
memset(head,-,sizeof(head));
size=;
memset(id,,sizeof(id));
memset(vis,,sizeof(vis));
} void add(int a,int b){
point[size]=b;
nxt[size]=head[a];
head[a]=size++;
id[b]++;
point[size]=a;
nxt[size]=head[b];
head[b]=size++;
id[a]++;
} void topo(){
queue<int>q;
for(int i=;i<=n;++i)if(id[i]==){
id[i]=-;
q.push(i);
}
while(!q.empty()){
int u=q.front();
q.pop();
for(int i=head[u];~i;i=nxt[i]){
int j=point[i];
id[j]--;
if(id[j]==){
id[j]=-;
q.push(j);
}
}
}
} int nnum=;
ll sum=; void dfs(int s){
vis[s]=;
nnum++;
sum+=v[s];
for(int i=head[s];~i;i=nxt[i]){
int j=point[i];
if(!vis[j]&&id[j]>)dfs(j);
}
} int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
for(int i=;i<=n;++i)scanf("%d",&v[i]);
while(m--){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
}
topo();
ll ans=;
for(int i=;i<=n;++i){
if(!vis[i]&&id[i]>){
sum=;
nnum=;
dfs(i);
if(nnum%&&nnum>)ans+=sum;
}
}
cout<<ans<<endl;
}
return ;
}