HDU - 5735 Born Slippy 思维 + dp(看题解)

HDU - 5735

感觉这个思路相当巧妙啊。。

考虑最普通的 dp[ i ] = max(dp[ j ] + w[ i ] opt w[ j ]), j 是 i 的祖先。

把(2 << 16) 分成前八位和后八位去优化最朴素的dp。 修改遍历后八位, 查询遍历前八位。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define ull unsigned long long
#define fi first
#define se second
#define mk make_pair
#define PLL pair<LL, LL>
#define PLI pair<LL, int>
#define PII pair<int, int>
#define SZ(x) ((int)x.size())
#define ALL(x) (x).begin(), (x).end()
#define fio ios::sync_with_stdio(false); cin.tie(0);

using namespace std;

const int N = (1 << 16) + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = (int)1e9 + 7;
const double eps = 1e-8;
const double PI = acos(-1);

template<class T, class S> inline void add(T& a, S b) {a += b; if(a >= mod) a -= mod;}
template<class T, class S> inline void sub(T& a, S b) {a -= b; if(a < 0) a += mod;}
template<class T, class S> inline bool chkmax(T& a, S b) {return a < b ? a = b, true : false;}
template<class T, class S> inline bool chkmin(T& a, S b) {return a > b ? a = b, true : false;}

int n, w[N], c[1 << 8];
LL dp[N], f[1 <<  8][1 << 8], g[N][1 << 8];
char op[10];
vector<int> G[N];

inline LL calc(int a, int b) {
    if(*op == ‘A‘) return a & b;
    else if(*op == ‘O‘) return a | b;
    else return a ^ b;
}

void dfs(int u) {
    dp[u] = 0;
    int now_a = w[u] >> 8;
    int now_b = w[u] & 255;
    for(int pre_a = 0; pre_a < 256; pre_a++) {
        if(c[pre_a]) {
            chkmax(dp[u], f[pre_a][now_b] + ((calc(now_a, pre_a)) << 8));
        }
    }
    for(int pre_b = 0; pre_b < 256; pre_b++) {
        g[u][pre_b] = f[now_a][pre_b];
    }
    c[now_a]++;
    for(int pre_b = 0; pre_b < 256; pre_b++) {
        chkmax(f[now_a][pre_b], dp[u] + calc(now_b, pre_b));
    }
    for(auto &v : G[u]) {
        dfs(v);
    }
    c[now_a]--;
    for(int pre_b = 0; pre_b < 256; pre_b++) {
        f[now_a][pre_b] = g[u][pre_b];
    }
}

int main() {
    int T; scanf("%d", &T);
    while(T--) {
        scanf("%d%s", &n, op);
        for(int i = 1; i <= n; i++) {
            G[i].clear();
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d", &w[i]);
        }
        for(int i = 2; i <= n; i++) {
            int pa; scanf("%d", &pa);
            G[pa].push_back(i);
        }
        dfs(1);
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            add(ans, 1LL * i * (dp[i] + w[i]) % mod);
        }
        printf("%d\n", ans);
    }
    return 0;
}

/*
*/

原文地址:https://www.cnblogs.com/CJLHY/p/11517001.html

时间: 2024-11-14 18:59:03

HDU - 5735 Born Slippy 思维 + dp(看题解)的相关文章

HDU 5735 Born Slippy(拆值DP+位运算)

[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5735 [题目大意] 给出一棵树,树上每个节点都有一个权值w,w不超过216,树的根为1,从一个点往根的方向走,可以得到他的祖先序列,现在需要从v1点的祖先序列中挑选出一定数量的点,组成数列v1,v2,v3……vm,要求vi是vi-1的祖先,求dp[v1]=max(dp[vi]+(w[v1] opt w[vi])),opt是一种运算,在题目中可为xor,or或者and,最后求出ans=sum_{i

HDU 5735 Born Slippy

看官方题解很详细了: 总结一下:递推式不难想到,但是每次求dp[x]需要枚举祖先,复杂度太高,需要优化. 题解的方法,可以使得复杂度降低到1<<24. #pragma comment(linker, "/STACK:1024000000,1024000000") #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<

Codeforces 750E New Year and Old Subsequence 线段树 + dp (看题解)

New Year and Old Subsequence 第一感觉是离线之后分治求dp, 但是感觉如果要把左边的dp值和右边的dp值合起来, 感觉很麻烦而且时间复杂度不怎么对.. 然后就gun取看题解了, 用线段树维护dp的值, 然后区间合并求答案. 每个节点保存dp[ i ][ j ]表示, 把当前管理的区间删到 s{2017}中的 s[ i + 1 ] - s[ j - 1 ],最少删几个, 然后合并的时候5 ^ 3合并. #include<bits/stdc++.h> #define L

HDU 6170 Two strings 思维 DP

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6170 题目描述: 两个字符串问是否匹配, '.'可以匹配任意字符, '*'可以使前一个数的出现次数成上一个自然数(0, 1, 2, 3........) 解题思路: DP, dp(i, j)表示A串匹配到j位, B串匹配到i位两个串是否匹配, 转移方程再代码里有, 参考Jaihk662的博客, 注释在代码中 代码: #include <iostream> #include <cstdio&

Codeforces 744C Hongcow Buys a Deck of Cards 状压dp (看题解)

Hongcow Buys a Deck of Cards 啊啊啊, 为什么我连这种垃圾dp都写不出来.. 不是应该10分钟就该秒掉的题吗.. 从dp想到暴力然后gg, 没有想到把省下的红色开成一维. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair&

HDU - 5380 Travel with candy (看题解)

HDU - 5380 感觉又是个脑洞题. 如果没有卖的情况感觉写过很多次了. 这题的关键在于你买糖的消耗只在加入队列的时候计算, 用不完的糖在最后退掉, 卖糖的盈利也包含在最后退糖中了, 我们只要把所有比当前sell值小的全部边成当前的sell值就行了, 相当于卖掉了. 感觉相当巧妙. #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #defin

Codeforces 983E NN country 思维 (看题解)

NN country 是1175E的加强树上版本, 大致思路是一样的.. 难点在于判断两个点是否被同一条线覆盖.. 居然没想出来. 我们先把所有点对都离线,对于点对(u, v) 我们dfs到 u 的时候 记录一下v子树的和为 t1, 然后把所有在 u 的线段的另一端 + 1, 向子树递归, 回溯的时候再求一下 v 子树的和为 t2 只要判断t1 是否等于 t2, 就知道有没有一条线段同时覆盖u,v. #include<bits/stdc++.h> #define LL long long #d

HDU 1231 最大连续子序列 DP题解

典型的DP题目,增加一个额外要求,输出子序列的开始和结尾的数值. 增加一个记录方法,nothing special. 记录最终ans的时候,同时记录开始和结尾下标: 更新当前最大值sum的时候,更新开始节点. const int MAX_N = 10001; long long arr[MAX_N]; int N, sta, end; long long getMaxSubs() { long long sum = 0, ans = LLONG_MIN; int ts = 0; for (int

HDU 1160 FatMouse&#39;s Speed DP题解

本题就先排序老鼠的重量,然后查找老鼠的速度的最长递增子序列,不过因为需要按原来的标号输出,故此需要使用struct把三个信息打包起来. 查找最长递增子序列使用动态规划法,基本的一维动态规划法了. 记录路径:只需要记录后继标号,就可以逐个输出了. #include <stdio.h> #include <algorithm> using namespace std; const int MAX_N = 1005; struct MouseSpeed { int id, w, s; b