|
由于该图不是一颗树,所以首先要将一些块压缩成点,所谓块就是没有割点的分量,于是只要求出割边,然后删除,之后对整个图进行Floodfill,得到的连通分量再用割边 补图,得到的图一定是一棵树,然后就可以树形DP了。 #include <iostream> #define MAXN 100010 using namespace std;
struct list { list *next; int ID; };
int n, m; int value[ MAXN ];
list data[ 1000000 ]; list *vec[ MAXN ]; list *Bri[ MAXN ]; int Ance[ MAXN ]; int Dep[ MAXN ]; int Color[ MAXN ]; int father[ MAXN ]; bool Cut[ MAXN ]; int num[ MAXN ]; #define G 0 // Grey 正在访问 #define B 1 // Black 访问完毕 #define W 2 // White 未曾访问 int root, T;
/**//*******************求无向图割边割点******************** * list vec[i] 表示原的邻接表 * * list Bri[i] 保存割边 * * bool Cut[i] 保存割点 * * int Ance[i] 保存和i或i的子孙邻接的最高辈分的祖先的深度 * * int Dep[i] 保存i点所在的深度 * * int Color[i] 保存当前结点访问情况 * * int father[i] 保存i的父亲结点的编号 * * int num[i] 缩点后每个点所在的新块的编号 * *********************************************************/
int Min ( int a, int b ) { return a < b ? a : b; }
void BridgeCut( int key, int depth ) {
int son = 0; Dep[ key ] = depth; Color[ key ] = G; Ance[ key ] = depth;
list *p, *q;
for( p = vec[ key ]->next; p ; p = p->next ) {
int next = p->ID;
if( next != father[ key ] && Color[ next ] == G) { Ance[ key ] = Min( Ance[ key ], Dep[ next ] ); }
if( Color[ next ] == W ) {
father[ next ] = key; BridgeCut( next, depth + 1); son ++; Ance[ key ] = Min( Ance[ key ], Ance[ next ] ); // 判割点 if( key == root ) { if( son > 1 ) { Cut[ key ] = true; } }else { if( Ance[ next ] >= Dep[ key ] ) { Cut[ key ] = true; } }
// 判割边 if( Ance[ next ] > Dep[ key ] ) { q = &data[ T++ ]; q->ID = next; q->next = Bri[ key ]->next; Bri[ key ]->next = q; q = &data[ T++ ]; q->ID = key; q->next = Bri[ next ]->next; Bri[ next ]->next = q; } } }
Color[ key ] = B; }
void CreateGraph() {
int i, x, y; T = 0;
for(i = 1; i <= n; i++) scanf("%d", &value[i]); for(i = 1; i <= n; i++) {
Bri[i] = &data[ T++ ]; Bri[i]->ID = i; Bri[i]->next = NULL;
vec[i] = &data[ T++ ]; vec[i]->ID = i; vec[i]->next = NULL;
Ance[i] = INT_MAX;
Cut[i] = false; Color[ i ] = W; }
list *p; while( m-- ) { scanf("%d %d", &x, &y);
p = &data[ T++ ]; p->next = vec[x]->next; p->ID = y; vec[x]->next = p;
p = &data[ T++ ]; p->next = vec[y]->next; p->ID = x; vec[y]->next = p; } }
/**//* 割边割点输出,以及缩块后的图的邻接表 */
void Print() { list *p; int i; for(i = 1; i <= n; i++) { printf("%d: ", i); for(p = Bri[i]->next; p; p = p->next) { printf("%d ", p->ID); } puts(""); } for(i = 1; i <= n; i++) { if( Cut[i] ) printf("point : %d\n", i); } }
/**//*以下是树形DP的过程*/
bool IsBridge(int u, int v) { list *p; for( p = Bri[u]->next; p ; p = p->next ) { if( p->ID == v ) return 1; } return 0; }
int F;
// 分块 void dfs( int key ) { list *p; num[ key ] = F; for(p = vec[ key ]->next; p ; p = p->next ) { if( Color[ p->ID ] == W && !IsBridge(key, p->ID) ) { Color[ p->ID ] = B; dfs( p->ID ); } } }
void FloodFill() { int i; for(i = 1; i <= n; i++) Color[i] = W; F = 1; for(i = 1; i <= n; i++) { if( Color[i] == W ) { Color[i] = B; dfs(i); F ++; } } }
__int64 AllVal[ MAXN ]; __int64 M, zong; list *New[ MAXN ]; int hash[ MAXN ];
// TreeDp void Search( int key ) { list *p; int son = 0;
for( p = New[ key ]->next; p; p = p->next) { int q = p->ID; if( hash[ q ] ) continue; son ++; hash[ q ] = 1; Search(q); AllVal[ key ] += (__int64)AllVal[ q ]; __int64 a = zong - AllVal[ q ]; __int64 b = AllVal[ q ]; a -= b; if( a < 0) a = -a; if( a < M ) M = a; } }
void TreeDp() {
int i; M = 0; memset( AllVal, 0, sizeof(AllVal) ); for(i = 1; i <= n; i++) { AllVal[ num[i] ] += (__int64)value[i]; M += (__int64)value[i]; } zong = M; F --; for(i = 1; i <= F; i++) { New[i] = &data[ T++ ]; New[i]->ID = i; New[i]->next = NULL; }
list *p, *q;
for(i = 1; i <= n; i++) { for(p = Bri[i]->next; p; p = p->next) { q = &data[ T++ ]; q->ID = num[ p->ID ]; q->next = New[ num[i] ]->next; New[ num[i] ]->next = q; } }
memset( hash, 0, sizeof(hash) ); hash[1] = 1; Search(1); }
int main() {
int i; int t = 1;
while( scanf("%d %d", &n, &m) != EOF && (n || m) ) { CreateGraph(); root = 1; father[ root ] = 0; BridgeCut( root, 0 ); FloodFill(); //Print(); TreeDp(); printf("Case %d: %I64d\n", t++, M); } return 0; }
|