树形dp
如果接暴力dp会超时,所以需要优化,dp[s]=max(dp[s],dp[i]+w[i] opt w[s]),其中i是s的祖先,那么暴力则是O(n^2)。
优化思路:
对于当前搜索到的点u,拥有数组ds(x,i),该数组储存ds(x,i)=max(dp[j]+ y opt i),其中x与y分别是j的前8位和后8位。
那么dp[u]=max(dp[u],ds(i,B)+((A opt i) << 8) ),就相当于预先计算了任意祖先j与后代可能出现的后八位的和并且对于相同前八位仅保留最值,后代计算dp时就可以枚举已经出现的前八位来利用这些信息。(文字表达能力差,具体看代码)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define ll unsigned int
const int maxn=(1<<16)+1;
const ll mod=1000000007;
ll f[256][256],ff[maxn][256],w[1000005];
int vis[256];
int head[1000005];
int ne[1000005];
int to[1000005];
int opt;
int cot;
void init(){
cot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v){
to[cot]=v;
ne[cot]=head[u];
head[u]=cot++;
}
ll getv(ll a,ll b){
if(opt==0)return a&b;
else if(opt==1)return a|b;
else return a^b;
}
ll ans;
void dfs(int u){
ll A=w[u]>>8,B=w[u]&0x000000ff;
ll dp=0,i;
for(i=0;i<256;i++)if(vis[i])dp=max(dp,f[i][B]+(getv(i,A)<<8));
ans=(ans+(1LL*(w[u]+dp)*u)%mod)%mod;
for(vis[A]++,i=0;i<256;i++){
ff[u][i]=f[A][i];
f[A][i]=max(f[A][i],dp+getv(B,i));
}
for(i=head[u];~i;i=ne[i])dfs(to[i]);
for(vis[A]--,i=0;i<256;i++)f[A][i]=ff[u][i];
}
char ch[3];
int main(){
int t,n;
int v;
scanf("%d",&t);
while(t--){
init();
scanf("%d %s",&n,ch);
//printf("%d %s\n",n,ch);
if(ch[0]=='A')opt=0;
else if(ch[0]=='O')opt=1;
else opt=2;
for(int i=1;i<=n;i++)scanf("%u",&w[i]);
for(int i=2;i<=n;i++){
scanf("%d",&v);
addedge(v,i);
}
ans=0;
dfs(1);
printf("%u\n",ans);
}
return 0;
}