zoukankan      html  css  js  c++  java
  • 【HDU】5735 Born Slippy DP+折半枚举优化

    题目链接

    题意

    给出一个具有 n 个节点的树,每个节点都有一个权值 w,现在对于每个节点 s 要求出一个(f(s))

    1. 对于节点 s,找到一个节点序列,(v_1,v_2,v_3...v_m)(v_1 = s)(v_{i+1})(v_i)的祖先节点
    2. (f(s)=w_s+sum_{i=2}^{m}{w_{v_i} opt w_{v_{i-1}}} f(s) 要尽可能的大)

    输出(sum_{i=1}^n i*f(i))

    思路

    我们用 (dp[i]) 表示(f(i)-w_i)

    先不考虑树上,把题目转移到一个整数数组上来。

    那么 (dp[i]=max(dp[i],dp[j]+w_j opt w_i)(j<i))

    树上同理,但是这个转移方程的复杂度太高了。

    愣是想不到如何把 (O(n^2)) 变成 (O(logn))

    看题解发现是真滴骚。

    在我理解来,就是把遍历 (j) 的过程变成了遍历 (w_j) 的可能取值,但是 (w_j)(2^{16})种情况,也不可能遍历,我们通过一个数组 (f[a][b]) 变得只需枚举 (2^8) 来降低复杂度 。

    总体复杂度为 (N*2^8)(2^{24})

    代码

    /*
     * @Autor: valk
     * @Date: 2020-08-11 12:38:37
     * @LastEditTime: 2020-09-28 17:19:07
     * @Description: 如果邪恶  是华丽残酷的乐章 它的终场 我会亲手写上 晨曦的光 风干最后一行忧伤 黑色的墨 染上安详
     */
    #include <algorithm>
    #include <iostream>
    #include <map>
    #include <math.h>
    #include <queue>
    #include <set>
    #include <stack>
    #include <stdio.h>
    #include <string.h>
    #include <string>
    #include <vector>
    #define pb push_back
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod = 1e9 + 7;
    const int seed = 12289;
    const double eps = 1e-6;
    const ll INF=0x8f8f8f8f8f8f8f8f;//-1886417009
    const int inf = 0x3f3f3f3f;
    const int N = 1e5 + 10;
    
    vector<int>vec[N];
    int w[N];
    ll rel[N][260],f[260][260],ans;
    char op[5];
    
    int opt(int a,int b)
    {
        if(op[0]=='A') return a&b;
        if(op[0]=='X') return a^b;
        return a|b;
    }
    
    void dfs(int pos)
    {
        int a=w[pos]>>8,b=w[pos]&255;
        ll maxn=0;
        for(int i=0;i<256;i++){
            if(f[i][b]!=INF)
                maxn=max(maxn,f[i][b]+(opt(i,a)<<8));
        }
        ans=(ans+1LL*(maxn+w[pos])%mod*pos%mod)%mod;
        for(int i=0;i<256;i++){
            rel[pos][i]=f[a][i];
            f[a][i]=max(f[a][i],maxn+opt(b,i));
        }
        for(int i=0;i<vec[pos].size();i++){
            dfs(vec[pos][i]);
        }
        for(int i=0;i<256;i++){
            f[a][i]=rel[pos][i];
        }
    }
    
    int main()
    {
        int T;
        scanf("%d",&T);
        while(T--){
            int n;
            scanf("%d%s",&n,op);
            ans=0;
            memset(f,INF,sizeof(f));
            for(int i=1;i<=n;i++){
                vec[i].clear();
            }
            for(int i=1;i<=n;i++){
                scanf("%d",&w[i]);
            }
            for(int i=2;i<=n;i++){
                int fa;
                scanf("%d",&fa);
                vec[fa].pb(i);
            }
            dfs(1);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    计算2*3+(2*(5+6)*3)/2+4*6的值
    单链表 删除倒数第m个元素的实现
    string.data()和string.c_str()
    c++ 打印时间
    二分查找
    两个整数相除的计算
    查看一个数字是不是回环数(对称)
    编译#include <stdio.h> 等用尖括号指定的文件提示找不到 VS googleTest 安装的makeInstall
    八皇后---递归
    分治算法--求m的n次方
  • 原文地址:https://www.cnblogs.com/valk3/p/13778120.html
Copyright © 2011-2022 走看看