1. 程式人生 > 實用技巧 >【HDU】5735 Born Slippy DP+折半列舉優化

【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\n",ans);
    }
    return 0;
}