J题:Defend Your Country
题目大意
solution
先讨论几种情况。
- 当n为偶数,则不需要删去任何的边,直接输出所有点的权值总和。(①)
- 当n为奇数,最简单的想法就是分成偶数和奇数的子图,并使奇数子图权值最小,而最理想的的情况是分出一个权值最小的点。设这个点为x。
- x不是割点,则将x单独后分开剩下的图仍联通且为偶数。(②)
- x是割点,所有与x联通的子图在分割后点的个数均为偶数。(③)
- x是割点,所有与x联通的子图中存在个数为奇数(④)
在上述几种情况中①可以直接输出。
然后我们猜想在n为奇数是否只需分割出1个点。
假设删去3个(或更多)点
在三个点构成的连通块上可以分割出偶数的更小连通块,而分割出单独点的权值必然小于整个块,最后只需分出一个点。
∴
\therefore
∴②③两种情况都可以将x点单独分开,然后权值和为
总
权
值
−
2
×
点
x
权
值
总权值-2\times点x权值
总权值−2×点x权值
所以n为奇数时只需遍历每个点,情况④时不合法。
然后求所有合法权值的最小值,
A
n
s
=
S
u
m
−
2
×
最
小
合
法
权
值
Ans=Sum-2\times 最小合法权值
Ans=Sum−2×最小合法权值。
c o d e \large code code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+7;
int n,m,x,y,w[N],dfn[N],low[N],num[N],o;
ll sum=0;
int minn=1<<30;
vector <int > v[N];
ll read()
{
ll num=0,f=1;
char ch=getchar();
while(ch>'9'||ch<'0')
if(ch=='-'){
f=-1;
ch=getchar();
}
while(ch<='9'&&ch>='0'){
num=num*10+ch-'0';
ch=getchar();
}
return num*f;
}
void Tarjan(int x,int fa)
{
bool flag=1;
vector <int > ::iterator it;
num[x]=1;
dfn[x]=low[x]=++o;
for(it=v[x].begin();it!=v[x].end();it++)
{
if(*it==fa) continue;
if(!dfn[*it])
{
Tarjan(*it,x);
low[x]=min(low[x],low[*it]);
num[x]+=num[*it];
if(low[*it]>=dfn[x]&&num[*it]&1)//当前点为割点,且分割完存在奇数点的联通块
flag=0;
}
else
low[x]=min(low[x],dfn[*it]);
}
if(flag)//更新w[x],求最小值
minn=min(minn,w[x]);
}
int main()
{
int T;
T=read();
while(T--)
{
n=read();m=read();
minn=1<<30;
sum=0;
for(int i=1;i<=n;i++){
w[i]=read();
v[i].clear();
sum+=w[i];
dfn[i]=0;
}
for(int i=1;i<=m;i++){
x=read();y=read();
v[x].push_back(y);
v[y].push_back(x);
}
if(n%2==0)
printf("%lld\n",sum);
else
{
o=0;
Tarjan(1,0);
printf("%lld\n",(sum-minn)-minn);
}
}
return 0;
}