HDU 5735 Born Slippy ( from:2016 Multi-University Training Contest 2 )

7 篇文章 0 订阅
5 篇文章 0 订阅

题意:给一棵树,且规定好了每个节点的父节点及子节点,并给出每个节点的值W(2<=W<=2^16),树的大小为n(2<=n<=2^16),对每一个节点s,求一个序列v1,v2,……,vm,使得:
1.v1=s,vi是vi-1的祖先;
2.这个序列使f(s)=Wv1+∑Wvi opt Wvi-1的值最大,其中opt是位运算和,或,异或三个运算中的一种,具体是哪个题目里会给出。
要求输出S=(∑i*f(i))mod(10^9+7)。(目前自己搞不来MathJax……公式这里有点乱,读者可以自己去原题地址看)

思路:一看就会有dp的想法,对一个点,找到其父节点中值最大的节点,进行运算,不断记录,不断运算。但是很烦恼的是这种思路的时间复杂度是O(n^2),明显会超时。看了题解后,知道了一个很巧妙的思想:设当前正在讨论的节点的值为W,基于W的值最大只有2^16,可以把W拆成两个部分:A,W二进制的前8位;B,W二进制的后8位。 用f[i][j]来表示当前节点的所有父节点中的W值的二进制的前8位为i的那些父节点,与j这个数进行opt运算后得到的dp最大值,如此,对每个点我们只需要枚举256次就ok了。
用dfs遍历这棵树不断更新,修改f数组,O(n*√n)就可出解。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long int LL;
const int MAXN=65540;
char op[10];
LL w[MAXN],v[256],f[256][256],h[MAXN][256],head[MAXN],nxt[MAXN];
LL ans;
inline LL opt(LL a,LL b){
    if(op[0]=='A') return a&b;
    if(op[0]=='O') return a|b;
    return a^b;
}
void dfs(LL x){
    LL dp=0,A=w[x]>>8,B=w[x]&255;
    for(int i=0;i<256;++i) if(v[i]) dp=max(dp,f[i][B]+(opt(A,i)<<8));
    ans=(1LL*x*(dp+w[x])+ans)%1000000007;
    ++v[A];
    for(int i=0;i<256;++i) h[x][i]=f[A][i],f[A][i]=max(f[A][i],opt(B,i)+dp);
    for(int i=head[x];i;i=nxt[i]) dfs(i);
    --v[A];
    for(int i=0;i<256;++i) f[A][i]=h[x][i];
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        LL x;
        scanf("%d",&n);
        scanf("%s",op);
        for(int i=1;i<=n;++i) scanf("%u",w+i),head[i]=0;
        for(int i=2;i<=n;++i) scanf("%u",&x),nxt[i]=head[x],head[x]=i;
        ans=0;
        dfs(1);
        printf("%u\n",ans);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值