2016多校第二场 HDU5735 神一样的dp,,,

可以先看一下多校的题解感受一下,,,

题意:

设dp[s]=f(s)-Ws;

则很容易设置dp方程dp[i]=max( dp[j] + Wi pot Wj )( j<i )  pot代表一种操作

显然硬来时间复杂度很高,因为是树形结构,dfs遍历的过程中有很多东西可以记录下来,所以想办法事先把dp[j]通通记录下来

因为数据范围是216 我们设置一个数组ds[x][b],表示当祖先结点的权值的前八位为x,当前结点权值后八位为b时 ds[x][b]= max{dp[j]+(Wj的后八位)pot(Wi的后八位)}————A

 则

dp[i]=max(dp[i] ,ds[x][y]+((x pot a)<<8));

其中j代表祖先节点,i代表当前节点,a代表Wi的前八位,通过遍历0~255相当于遍历了所有的祖先节点的前八位,而当碰到祖先节点时就会进行更新dp[i] 的操作

在更新完dp[i]之后再更新ds[x][y]具体看代码,当然有可能对于一个结点来说ds[x][y]更新取得的值并不是这个结点的祖先的值,这个问题可以因为给出的图是树形结构而比较方便解决

总体来说就是用权值前八位即0~255的遍历代替了对于每一个祖先结点的遍历,让复杂度下降到(28n)然后就过题了

再具体一点就是说ds[x][y]记录的是所有的祖先结点的前八位为x时A式的最大值,这就省去了几个前八位同时为x时节点之间的比较

最后说一句,膜膜膜膜膜膜膜大神

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
#include <math.h>
using namespace std;

const int MOD = 1e9 + 7;
const int N=1<<17;
typedef long long ll;
int t,n,op,vis[300],cnt;
ll w[N],dx[300][300],unuse[N][300],ans;
char ss[10];
std::vector<ll>g[N];
ll opt(ll a,ll b){
if(op==1) return a^b;
if(op==2) return a&b;
if(op==3) return a|b;
}
void dfs(ll u){
 ll a=w[u]>>8,b=w[u]&255,dp=0;
 for(ll i=0;i<256;i++)
 if(vis[i]) dp=max(dp,dx[i][b]+(opt(a,i)<<8));//vis是为了记录当前节点的祖先中是否有前八位为i的
 ans=ans+(u*((dp+w[u])%MOD))%MOD;
 ans%=MOD;
//cout<<ans<<endl;
 vis[a]++;//接下来要访问u的字数所以vis[a]++
 for(ll i=0;i<256;i++){
    unuse[u][i]=dx[a][i]; //将访问u之前的dx[a][i]记录下来,当u的子树访问完之后还原以消除u的影响
    dx[a][i]=max(dx[a][i],dp+opt(b,i));//对u的子树更新dx
 }

 for(int i=0;i<g[u].size();i++){
    ll v=g[u][i];
    dfs(v);
 }
 for(int i=0;i<256;i++)
    dx[a][i]=unuse[u][i];//还原dx
    vis[a]--;//表示u已经访问完它的前八位的vis减掉
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>t;
while(t--){
   for(int i=0;i<N;i++)
      g[i].clear();
   memset(vis,0,sizeof(vis));
   memset(dx,0,sizeof(dx));
    scanf("%d",&n);
    getchar();
    scanf("%s",ss);
  // cout<<ss<<endl;

    if(ss[0]=='X') op=1;
    if(ss[0]=='A') op=2;
    if(ss[0]=='O') op=3;
    int i;
    for(i=1 ;i<=n; i++)
        scanf("%lld",&w[i]);
    for(i=2;i<=n;i++){
        ll j;
        scanf("%lld",&j);
        g[j].push_back(i);
    }
    ans=0;
   // cout<<"r"<<endl;
    dfs(1);
    cout<<ans<<endl;
 }

return  0;
}

ancestor of imax​​{dp(j)+wi​​ and wj​​}. 然后, 显然dp(j)+wi and wjdp(j)+w_i ext{ and }w_jdp(j)+wi​​ and wj​​这个式子可以拆成dp(j)dp(j)dp(j)+[wiw_iwi​​后8位] and [wjw_jwj​​后8位] + ([wiw_iw​i​​前8位] and [wjw_jw​j​​前8位]) << 8.

 

S=(ni=1if(i))mod(109+7).

S=(ni=1if(i)) mod (109+7).

2n216,0wi<216.

2n216,0wi<216.

原文地址:https://www.cnblogs.com/shimu/p/5706781.html