//10344K 391MS C++
#include <cstdio>
#include <cstring>
#include <cmath>
const long long MAX = 100005;
long long adjacentListHead[MAX];
long long studentTotalSum;
struct Node{
long long studentNum;
};
long long Idx;
struct ConnectedNode{
int curNodeId;
int nextNodePos;
};
typedef struct ConnectedNode ConnectedNode;
ConnectedNode adjacentNodes[1000005];
typedef struct Node Node;
Node nodes[MAX];
long long DP[MAX];
long long uNum;
long long connectionNum;
long long minDiffer = -1;
inline long long MIN(long long A, long long B) {
return A < B ? A: B;
}
void addInAdjacentList(int nodeId, int adjacentNodeId) {
Idx++;
adjacentNodes[Idx].curNodeId = adjacentNodeId;
adjacentNodes[Idx].nextNodePos = adjacentListHead[nodeId];
adjacentListHead[nodeId] = Idx;
}
inline long long ABS(long long A) {
return A > 0? A: -A;
}
void dfs(long long curNodeId, long long prevNodeId) {
// visited[curNodeId] = 1;
long long adjacentNodePos = adjacentListHead[curNodeId];
long long adjacentNodeId = adjacentNodes[adjacentNodePos].curNodeId;
long long adjacentNum = 0;
while(adjacentNodeId) {
// prlong longf("A adjacentNodeId: %d curNodeId: %d prevNodeId: %d\n", adjacentNodeId, curNodeId, prevNodeId);
if (adjacentNodeId != prevNodeId) {
dfs(adjacentNodeId, curNodeId);
adjacentNum++;
DP[curNodeId] += DP[adjacentNodeId];
}
adjacentNodePos = adjacentNodes[adjacentNodePos].nextNodePos;
adjacentNodeId = adjacentNodes[adjacentNodePos].curNodeId;
}
DP[curNodeId] += nodes[curNodeId].studentNum;
// prlong longf("%lld %d\n", DP[curNodeId], curNodeId);
if (minDiffer == -1) {
minDiffer = ABS(studentTotalSum - 2*DP[curNodeId]);
} else {
minDiffer = MIN(minDiffer, ABS(studentTotalSum - 2*DP[curNodeId]));
}
}
long long caseId = 1;
int main() {
while(scanf("%lld %lld", &uNum, &connectionNum) != EOF) {
if (!uNum && !connectionNum) {
return 0;
}
Idx = 0;
studentTotalSum = 0;
minDiffer = -1;
memset(DP, 0, sizeof(DP));
// memset(visited, 0, sizeof(visited));
memset(adjacentListHead, 0, sizeof(adjacentListHead));
memset(adjacentNodes, 0, sizeof(adjacentNodes));
for (long long i = 1; i <= uNum; i++) {
scanf("%lld", &(nodes[i].studentNum));
studentTotalSum += nodes[i].studentNum;
}
for (long long i = 1; i <= connectionNum; i++) {
int A, B;
scanf("%d %d", &A, &B);
addInAdjacentList(A, B);
addInAdjacentList(B, A);
}
dfs(1, -1);
printf("Case %lld: %lld\n", caseId, minDiffer);
caseId++;
}
}
水题,虽然挂着树状DP的名头,不过是非常简单的运用,倒是因为数据值太大引起了不少RE 和 WA。
题目要求的其实说白了就是,给你一个无根树,然后选择树种中的一条边断开,使得形成的两个子树的权值和的差的绝对值最小。
因为是无根树,所以完全可以任选一个点作为根,而因为题目的输入没有给出两个点的父子关系,并且树的节点非常多,因此在保存该树的时候,要用图的邻接表来表示(好久没用,邻接表的预分配数组开小了,RE了一次),在题目输入的时候,就先累加得到整个树的权值和S, 然后,就是选择一个点作为root(直接选了1),
那么下面就是DP了,DP[i]表示以i为根的子树的权值和,那么很简单的,DP[i] = SUM{DP[i的子节点]}, 注意,因为用的是邻接表表示的树,因此,实际的遍历点i的所有子节点,是通过遍历与i邻接的所有节点实现的,但是在这些邻接的点中,会有一个点其实是i的父节点,这个要区分开,可以在dfs()中多加一个参数P,表示此次dfs的节点i,其父节点是P,这样在遍历i的邻接点时,可以直接将p跳过。
每次得到一个子树的权值和时(即DP[i]),就算一个 abs(S - 2*DP[i])(就是以i为根的子树权值和 与 剩下的点组成的子树的权值和的差的绝对值), 在每次的dfs中,保存最小的abs(S - 2*DP[i]);
最后输出该最小值即可。