Step1 Problem:
给你一棵 n 个节点的树以及点权 w[i], 给你 opt, 定义 f(s) 为 s 的祖先序列(包括 s) opt 和最大值 + w[s].
输出 S = sum(s * f(s)), s = 1, 2, 3, …, n.
数据范围:
2 <= n <= 2^16, 0 <= w[i] < 2^16, opt = 与 或 异或 其中之一.
Step2 Ideas:
学习博客
正常思路:dp[i] = max(dp[i], dp[j] + opt(w[i], w[j])); O(n^2) 显然超时
dp[j] + opt(w[i], w[j]) = dp[j] + (opt(w[i]前八位, w[j]前八位)<<8) + opt(w[i]后八位, w[j]后八位);
我们可以先求出 dp[j] + opt(w[i]后八位, w[j]后八位) 或者 dp[j] + (opt(w[i]前八位, w[j]前八位)<<8) 由于学习博客是 dp[j] + opt(w[i]后八位, w[j]后八位) 所以我们将这个,另外一个也是可行的。
令:ds[x][y] = max(dp[x][y], dp[j] + opt(w[i]后八位, w[j]后八位));
第一步:求 ds[x][y]; 已知 dp[j], w[j]后八位; 枚举 w[i] 后八位 O(256)的复杂度求出 ds[x][y];
第二步:求 dp[i]; 已知 ds[x][y], w[i]前八位; 枚举 w[j] 前八位 O(256)的复杂度求出 dp[i];
时间优化成了 O(n^(3/2))
讨论 ds[x][y]:x 和 y 分别代表什么
我们需要 第一步和第二步有联系:
所以 x 和 y 得是枚举的对象:x 代表 w[j] 前八位, y 代表 w[i] 后八位. 反过来也行.
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = (1<<16)+5;
const int M = 1<<8;
const int MOD = 1e9+7;
int w[N], fq[N], n;
ll ds[M][M], now[N][M];//ds[x][y] x w[j]前八位, y w[i]后八位, dp[j] + opt(w[j]&255, w[i]&255)
int vis[M];
ll ans;
vector<int> Map[N];
char op[50];
int opt(int x, int y)
{
if(op[0] == 'A') return x&y;
else if(op[0] == 'X') return x^y;
else return x|y;
}
void dfs(int u)
{
int A = w[u]>>8, B = w[u]&255;
ll dp = 0;
for(int i = 0; i < 256; i++)//当前是 i, 枚举 w[j] 前八位
if(vis[i]) dp = max(dp, ds[i][B]+(opt(i, A)<<8));
ans += (1LL*(dp+w[u])%MOD*u)%MOD, ans %= MOD;
for(int i = 0; i < 256; i++)//当前是 j, 枚举 w[i] 的后八位
{
now[u][i] = ds[A][i];
ds[A][i] = max(ds[A][i], dp+opt(B, i));
}
vis[A]++;
for(int i = 0; i < Map[u].size(); i++)
{
int to = Map[u][i];
dfs(to);
}
vis[A]--;
for(int i = 0; i < 256; i++)
ds[A][i] = now[u][i];
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
ans = 0;
scanf("%d %s", &n, op);
for(int i = 1; i <= n; i++)
{
scanf("%d", w+i);
Map[i].clear();
}
for(int i = 2; i <= n; i++)
{
scanf("%d", &fq[i]);
Map[fq[i]].push_back(i);
}
memset(vis, 0, sizeof(vis));
memset(ds, 0, sizeof(ds));
dfs(1);
printf("%lld\n", ans);
}
return 0;
}