题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5735
题意:
给定树及每个点的权值,对于每个节点从该结点开始从下到上找若干祖先结点,使得得到的序列中相邻结点之间对于特定的位运算的和最大。求每个节点得到的结果加上自身权值的值。
分析:
首先我们可以得到最直接的状态定义
dp[i]:=到达i结点时能得到的最大值
。
状态转移方程
dp[i]=max{dp[j]+w[j] opt w[i]},其中j为i的祖先
对于每个
i
直接枚举
由于
i
最大为
假设我们只显示的考虑祖先
j
的前八位的对
然后我们再用求出的
dp
更新
dx
数组,此时显式的考虑后八位,来更新
i
对其后辈节点的影响,
最后树上的操作我们边更新边备份,
dfs
记录对应结点
i
对后辈节点的影响,回溯时还原即可。
现在我们来分析一下完整的过程。最初从根结点开始初始
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int, int>p;
#define sa(n) scanf("%d", &(n))
const int maxn = (1 << 16) + 5, maxm = (1 << 8) + 5, mod = 1e9 + 7, oo = 0x3f3f3f3f;
int w[maxn];
int head[maxn];
struct EDGE{
int to; int next;
}edge[maxn];
char op[5];
inline int get(int a, int b)
{
if(op[0] == 'A') return a & b;
if(op[0] == 'X') return a ^ b;
else return a | b;
}
ll dp[maxm][maxm];
ll temp[maxn][maxm];
ll ans = 0;
void dfs(int x)
{
int a = w[x] >> 8, b = w[x] & 255;
ll tmp = 0;
for(int i = 0; i < 256; i++){
if(dp[i][b] != -1){
tmp = max(tmp, dp[i][b] + (get(a, i) << 8));
}
}
(ans += (tmp + w[x]) * 1ll * x) %= mod;
for(int i = 0; i < 256; i++){
temp[x][i] = dp[a][i];
dp[a][i] = max(dp[a][i], tmp + get(i, b));
}
for(int i = head[x]; i != -1; i = edge[i].next){
dfs(edge[i].to);
}
for(int i = 0; i < 256; i++) dp[a][i] = temp[x][i];
}
int tot;
void addedge(int a, int b)
{
edge[tot].to = b;
edge[tot].next = head[a];
head[a] = tot++;
}
int main (void)
{
int T;sa(T);
while(T--){
int n;sa(n);
scanf("%s", op);
tot = 0;
memset(dp, -1, sizeof(dp));
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; i++){
scanf("%d", &w[i]);
}
int x;
for(int i = 2; i <= n; i++){
scanf("%d", &x);
addedge(x, i);
}
ans = 0;
dfs(1);
printf("%I64d\n", ans);
}
return 0;
}