题目大意:求将一张无向图(n个点,m条边)移除一条边分为不连通两部分,使得两部分的点权和最接近,若无法分为两部分,则输出impossible。
题解:拿到题面还算清晰,就是先tarjan
缩点,因为边双连通分量肯定无法移除一条边使得分为不连通的两部分(因为是无向图),然后重新建图,附好点权,就可以开始愉快地跑dfs
了,然后不断比较取min
即可。但是wa了将近五发之后(检查完了笔误细节),笔者不服了,这个方法肯定是没有问题的,那么问题在哪?笔者发现,这题编号竟然是从0~n-1,我真的说不出话,审题不仔细,简直写到难过,以为改了能对之后,又wa了一发,笔者静下来检查,确保无误后,又提交了一次,又wa了,于是笔者去hdu该题的discuss
找到了一组样例,不得不说,wa得心服口服,因为题目输入可能有重边,即0 1 , 1 0
,因为是无向图所以存边肯定调用了两次_add(u,v)
,所以等于0 1
之间,有了四条边,如果if (v==pre) then continue
,那么就会出错,这个点可以说是很坑人了,只能说是自己疏忽了,但是这个也很好解决,只要多加个flag
判断就好。其实也就是两个点的边双连通要考虑。(个人觉得两个点不可能叫边双连通,暂且这样称作吧,即上文0 1 , 1 0
)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#define mem(a,b) memset((a),(b),sizeof(a))
using namespace std;
const int N = 1e4 + 16;
struct Edge
{
int u, v, nxt;
};
Edge edge[N<<1], edge2[N<<1];
bool vis[N];
int head[N], ecnt;
int head2[N], ecnt2;
int sta[N], dfn[N], low[N], col[N];
int val[N], vv[N], f[N];
int top, dep, sum;
int n, m;
void _add( int u, int v )
{
edge[ecnt].u = u;
edge[ecnt].v = v;
edge[ecnt].nxt = head[u];
head[u] = ecnt ++;
}
void _add2( int u, int v )
{
edge2[ecnt2].u = u;
edge2[ecnt2].v = v;
edge2[ecnt2].nxt = head2[u];
head2[u] = ecnt2 ++;
}
void init()
{
mem(head,-1);
mem(vis,0);
mem(sta,0);
mem(dfn,0);
mem(low,0);
mem(val,0);
top = sum = dep = ecnt = 0;
}
void tarjan( int u, int pr )
{
sta[++top] = u;
low[u] = dfn[u] = ++dep;
vis[u] = 1;
int flag = 1;
for ( int i = head[u]; i+1; i = edge[i].nxt )
{
int v = edge[i].v;
if ( v == pr && flag )
{
flag = 0;
continue;
}
if ( !dfn[v] )
{
tarjan( v, u );
low[u] = min( low[u], low[v] );
}
else if ( vis[v] )
low[u] = min( low[u], low[v] );
}
if ( low[u] == dfn[u] )
{
col[u] = ++sum;
vis[u] = 0;
while ( sta[top] != u )
{
col[sta[top]] = sum;
vis[sta[top--]] = 0;
}
top --;
}
}
void dfs( int u )
{
if ( vis[u] ) return ;
vis[u] = 1;
f[u] += vv[u];
for ( int i = head2[u]; i+1; i = edge2[i].nxt )
{
int v = edge2[i].v;
if ( !vis[v] )
{
dfs(v);
f[u] += f[v];
}
}
}
int main()
{
while ( ~scanf("%d%d", &n, &m) )
{
init();
int pow = 0;
for ( int i = 0; i < n; i ++ )
{
scanf("%d", &val[i]);
pow += val[i];
}
for ( int i = 0; i < m; i ++ )
{
int u, v;
scanf("%d%d", &u, &v);
_add(u,v);
_add(v,u);
}
for ( int i = 0; i < n; i ++ )
if ( !dfn[i] )
tarjan(i, -1);
if ( sum == 1 )
{
puts("impossible");
continue;
}
mem(head2,-1);
ecnt2 = 0;
mem(vv,0);
mem(f,0);
for ( int i = 0; i < n; i ++ )
{
vv[col[i]] += val[i];
for ( int j = head[i]; j+1; j = edge[j].nxt )
{
int v = edge[j].v;
if ( col[i] != col[v] )
_add2(col[i],col[v]);
}
}
mem(vis,0);
dfs(1);
int ans = pow;
for ( int i = 1; i <= sum; i ++ )
ans = min( ans, abs( pow - f[i] - f[i] ) );
// cout << "sum: " << sum << endl;
printf("%d\n", ans);
}
return 0;
}