Description
给出一张n个点m条边的无向图,每个点有一个点权,删掉所有度数小于等于2的点之后,问点数为奇数的连通块中点权之和
Input
第一行一整数T表示用例组数,每组用例首先输入两个整数n和m表示点数和边数,然后输入n个整数vi表示每个点的点权,最后m行每行两个整数u和v表示u和v之间有一条边(1<=T<=30,1<=n<=10^4,1<=m<=10^5)
Output
对于每组用例,输出删完点后点数为奇数的连通块中点权之和
Sample Input
1
7 7
1 2 3 4 5 6 7
1 4
1 5
4 5
2 3
2 6
3 6
2 7
Sample Output
21
Solution
先按拓扑排序的思想删点,删完点后重新建图,然后dfs找连通块,点数为奇的就将点权累加到结果中
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
typedef long long ll;
#define maxn 11111
int T,n,m,res,cnt,vis[maxn],w[maxn];
vector<int>G[maxn],g[maxn];
ll sum;
void dfs(int u)
{
vis[u]=res,cnt++,sum+=w[u];
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(vis[v])continue;
dfs(v);
}
}
queue<int>q;
int d[maxn],used[maxn];
void deal(int n)
{
while(!q.empty()) q.pop();
memset(used,0,sizeof(used));
for(int i=1;i<=n;i++)
{
d[i]=G[i].size();
if(d[i]<2)q.push(i);
}
while(!q.empty())
{
int u=q.front();
q.pop();
used[u]=1;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(!used[v])
{
d[v]--;
if(d[v]<2)q.push(v);
}
}
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&w[i]),G[i].clear(),g[i].clear();
while(m--)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
deal(n);
for(int i=1;i<=n;i++)
if(!used[i])
for(int j=0;j<G[i].size();j++)
{
int k=G[i][j];
if(used[k])continue;
g[i].push_back(k);
}
memset(vis,0,sizeof(vis));
ll ans=0;
res=0;
for(int i=1;i<=n;i++)
{
res++,cnt=0;sum=0;
if(!vis[i]&&!used[i])dfs(i);
if(cnt&1)ans+=sum;
}
printf("%I64d\n",ans);
}
return 0;
}