http://acm.hdu.edu.cn/showproblem.php?pid=5735
题意:给你一棵树,每个点有权值,然后让你求n个函数
f[i]
,这个函数是让你选一些数,
f[i]
是从i点开始,往它的祖先找,使得相邻两点的位运算之和加上i点的权值,最大。
题解:这题首先上来就一脸蒙逼,这里用或运算做例子,其实就是个树形dp,从1点开始,然后走到一个节点i,找这个节点的祖先j,使得
dp[i]=max(dp[j]+w[i]|w[j])
,但是怎么找,就需要枚举了,就炸了
题解的姿势很高级,另外开一个数组叫做ds,
ds[x][y]
表示
wi
的后8位为x,
wj
的前8位为y,并且
ds[x][y]=max(dp[j]+(w[i]>>8)|(w[j]>>8))
,于是我们假设
w[i]=(a<<8)|b
,
dp[i]=max(ds[b][j]+(j|a)<<8)
,这样我们只要维护ds这个数组,然后求dp[i]的时候,只要枚举
w[j]
的前8位是多少,即可转移,复杂度最多是
O(224)
。
然后维护ds这个数组也是一样,就是用上面的方法,求完dp[i]之后,要用w[i]的前8位,枚举以后一个东西的后八位,来维护ds数组,
ds[j][a]=max(dp[i]+j|b)
,但是之前需要给
ds[j][a]
这个数组做可持久化,因为,网下搜的时候是用这会更新了的这个ds数组,然后如果回溯到父节点,走到了别的路去,那么这会这个更新的ds数组就不能用了,所以要在更新ds数组前,先把当前状态保存到一个数组里,然后等到下面的都搜完了,再还原状态,然后回溯上去。
很劲的一个树形dp,回味无穷
代码:
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
using namespace std;
#define MAX 1005
#define MAXN 1000005
#define maxnode 15
#define sigma_size 30
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lrt rt<<1
#define rrt rt<<1|1
#define middle int m=(r+l)>>1
#define LL long long
#define ull unsigned long long
#define mem(x,v) memset(x,v,sizeof(x))
#define lowbit(x) (x&-x)
#define pii pair<int,int>
#define bits(a) __builtin_popcount(a)
#define mk make_pair
#define limit 10000
//const int prime = 999983;
const int INF = 0x3f3f3f3f;
const LL INFF = 0x3f3f;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-8;
const LL mod = 1e9+7;
const ull mx = 133333331;
/*****************************************************/
inline void RI(int &x) {
char c;
while((c=getchar())<'0' || c>'9');
x=c-'0';
while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}
/*****************************************************/
char op[10];
int val[(1<<16)+5];
vector<int> v[(1<<16)+5];
LL ds[(1<<8)+5][(1<<8)+5];
LL dp[(1<<16)+5];
LL back[(1<<16)+5][(1<<8)+5];
int opt(int a,int b){
if(op[0]=='A') return a&b;
else if(op[0]=='X') return a^b;
else return a|b;
}
void dfs(int u){
LL tmp=0;
int a=val[u]>>8,b=val[u]&255;
for(int i=0;i<256;i++){
if(ds[b][i]!=-1) tmp=max(tmp,ds[b][i]+opt(i,a)*256);
}
dp[u]=tmp+val[u];
for(int i=0;i<256;i++) back[u][i]=ds[i][a];
for(int i=0;i<256;i++){
ds[i][a]=max(ds[i][a],tmp+opt(b,i));
}
for(int i=0;i<v[u].size();i++) dfs(v[u][i]);
for(int i=0;i<256;i++) ds[i][a]=back[u][i];
}
int main(){
//freopen("in.txt","r",stdin);
int t;
cin>>t;
while(t--){
int n;
scanf("%d%s",&n,op);
for(int i=1;i<=n;i++) scanf("%d",&val[i]),v[i].clear();
for(int i=2;i<=n;i++){
int a;
scanf("%d",&a);
v[a].push_back(i);
}
mem(ds,-1);
dfs(1);
LL ans=0;
for(int i=1;i<=n;i++){
ans=(ans+i*dp[i])%mod;
}
cout<<ans<<endl;
}
return 0;
}
本文介绍了一道关于树形动态规划的算法题,通过巧妙地利用位运算和动态规划技巧,解决了一个复杂的函数最优化问题。文章详细解析了解题思路和实现细节。
687

被折叠的 条评论
为什么被折叠?



