Born Slippy
Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 1049 Accepted Submission(s): 307
Problem Description
Professor Zhang has a rooted tree, whose vertices are conveniently labeled by
1,2,...,n. And the
i-th vertex is assigned with weight
w_i.
For each s \in \{1,2,...,n\}, Professor Zhang wants find a sequence of vertices v_1,v_2,...,v_m such that:
1. v_1=s and v_i is the ancestor of v_{i-1} (1 < i \le m).
2. the value f(s)=w_{v_1}+\sum\limits_{i=2}^{m}w_{v_i} \text{ opt } w_{v_{i-1}} is maximum. Operation x \text{ opt } y denotes bitwise AND, OR or XOR operation of two numbers.
For each s \in \{1,2,...,n\}, Professor Zhang wants find a sequence of vertices v_1,v_2,...,v_m such that:
1. v_1=s and v_i is the ancestor of v_{i-1} (1 < i \le m).
2. the value f(s)=w_{v_1}+\sum\limits_{i=2}^{m}w_{v_i} \text{ opt } w_{v_{i-1}} is maximum. Operation x \text{ opt } y denotes bitwise AND, OR or XOR operation of two numbers.
Input
There are multiple test cases. The first line of input contains an integer
T
, indicating the number of test cases. For each test case:
The first line contains an integer n and a string opt (2 \le n \le 2^{16}, opt \in \{\text{AND}, \text{OR}, \text{XOR}\} ) -- the number of vertices and the operation. The second line contains n integers w_1,w_2,...,w_n (0 \le w_i < 2^{16}) . The thrid line contain n-1 integers f_2,f_3,...,f_n (1 \le f_i < i) , where f_i is the father of vertex i .
There are about 300 test cases and the sum of n in all the test cases is no more than 10^6 .
The first line contains an integer n and a string opt (2 \le n \le 2^{16}, opt \in \{\text{AND}, \text{OR}, \text{XOR}\} ) -- the number of vertices and the operation. The second line contains n integers w_1,w_2,...,w_n (0 \le w_i < 2^{16}) . The thrid line contain n-1 integers f_2,f_3,...,f_n (1 \le f_i < i) , where f_i is the father of vertex i .
There are about 300 test cases and the sum of n in all the test cases is no more than 10^6 .
Output
For each test case, output an integer
S=(\sum\limits_{i=1}^{n}{i \cdot f(i)}) \text{ mod } (10^9 + 7)
.
Sample Input
3 5 AND 5 4 3 2 1 1 2 2 4 5 XOR 5 4 3 2 1 1 2 2 4 5 OR 5 4 3 2 1 1 2 2 4
Sample Output
91 139 195
Author
zimpha
Source
【HDU5735 2016 Multi-University Training Contest 2B】【暴力做法】Born Slippy 祖先链的最大运算权值
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = (1 << 16) + 10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n; char s[10];
int w[N], g[N];
vector<int>a[N];
int op(int x, int y)
{
if (s[0] == 'X')return x ^ y;
if (s[0] == 'A')return x & y;
if (s[0] == 'O')return x | y;
}
LL dv[N]; //记录某一深度节点最优前导值
LL dw[N]; //记录某一深度节点的自值
LL ANS;
void dfs(int x, int fa, int dep)
{
dv[dep] = 0;
for (int i = max(1, dep - 16); i < dep; ++i)
{
gmax(dv[dep], dv[i] + op(w[x], dw[i]));
}
dv[dep];
dw[dep] = w[x];
ANS += (LL)x * (dv[dep] + w[x]);
for (int i = a[x].size() - 1; ~i; --i)
{
int y = a[x][i];
if (y == fa)continue;
dfs(y, x, dep + 1);
}
}
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &n); scanf("%s", s);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &w[i]);
a[i].clear();
}
for (int i = 2; i <= n; ++i)
{
int x; scanf("%d", &x);
a[x].push_back(i);
}
ANS = 0;
dfs(1, 0, 1);
printf("%lld\n", ANS%Z);
}
return 0;
}
/*
【trick&&吐槽】
妈蛋
大力出奇迹
竟然因为卡题让自己没时间大力!
浑身难受。
读错题了——
ancestor是祖先而不一定是父节点。
【题意】
有n(2^16)个点。
每个点都有一个权值,
它们呈现出一棵树的形态。
我们想对于每一个节点s(s∈[1,n])
找到一个序列v1,v2,...,vm,
使得——
1,v[1]=s
2,v[i]是v[i-1]的父节点
3,f(s)=w[v1] + w[vi] opt w[vi-1]
【类型】
暴力
【分析】
这道题虽然我们很容易构造卡掉暴力的数据
但是出题人zimpha太懒了,数据都是随机的。
在这种情况下,每个节点以距离他最近的16个祖先为前驱更新答案,这道题就可以AC啦。
不过正解是——
用f[i][j]表示,在该节点的低8位为j,且其祖先节点的高8位为i的情况下,其所能获得的最大前驱状态值。
那么,对于每个节点,假设其高8位为a,低8位为b,
我们枚举其祖先节点的前8位i,就可以通过f[i][b]得到该点的最优状态值。
然后我们再更新f[a][0~255]
【时间复杂度&&优化】
O(暴力) -> O(n * 2^8)
*/
【HDU5735 2016 Multi-University Training Contest 2B】【折半预处理做法】Born Slippy 祖先链的最大运算权值
#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
const int N = (1 << 16) + 10, M = 0, Z = 1e9 + 7, ms63 = 0x3f3f3f3f;
int casenum, casei;
int n; char s[10];
int w[N];
vector<int>e[N];
int op(int x, int y)
{
if (s[0] == 'X')return x ^ y;
if (s[0] == 'A')return x & y;
if (s[0] == 'O')return x | y;
}
LL f[256][256]; //f[i][j]表示其后八位为j,其祖先的前8位为i的条件下,其所能获得的最优后8位的权值
LL tmp[N][256]; //tmp[i][j]用于暂存"在节点i,状态j"对其子孙的影响生效前的f[w[x]前8位][sta]
LL ANS;
void dfs(int x, int fa)
{
int a = w[x] >> 8;
int b = w[x] & 255;
LL val = 0;
for (int i = 0; i < 256; ++i)
{
gmax(val, f[i][b] + (op(i, a) << 8));
}
ANS += x* (val + w[x]);
MC(tmp[x], f[a]);
for (int i = 0; i < 256; ++i)
{
gmax(f[a][i], val + op(b, i));
}
for (int i = e[x].size() - 1; ~i; --i)
{
int y = e[x][i];
if (y != fa)dfs(y, x);
}
MC(f[a], tmp[x]);
}
int main()
{
scanf("%d", &casenum);
for (casei = 1; casei <= casenum; ++casei)
{
scanf("%d", &n); scanf("%s", s);
for (int i = 1; i <= n; ++i)
{
scanf("%d", &w[i]);
e[i].clear();
}
for (int i = 2; i <= n; ++i)
{
int x; scanf("%d", &x);
e[x].push_back(i);
}
MS(f, -63);
ANS = 0;
dfs(1, 0);
printf("%lld\n", ANS%Z);
}
return 0;
}
/*
【trick&&吐槽】
妈蛋
大力出奇迹
竟然因为卡题让自己没时间大力!
浑身难受。
读错题了——
ancestor是祖先而不一定是父节点。
注意,这题的答案,在不取模的条件下,最多为——
2^16*2^16,是会爆int的。
【题意】
有n(2^16)个点。
每个点都有一个权值,
它们呈现出一棵树的形态。
我们想对于每一个节点s(s∈[1,n])
找到一个序列v1,v2,...,vm,
使得——
1,v[1]=s
2,v[i]是v[i-1]的父节点
3,f(s)=w[v1] + w[vi] opt w[vi-1]
【类型】
暴力
【分析】
这道题虽然我们很容易构造卡掉暴力的数据
但是出题人zimpha太懒了,数据都是随机的。
在这种情况下,每个节点以距离他最近的16个祖先为前驱更新答案,这道题就可以AC啦。
不过正解是——
用f[i][j]表示,在该节点的低8位为j,且其祖先节点的高8位为i的情况下,其所能获得的最大前驱状态值。
那么,对于每个节点,假设其高8位为a,低8位为b,
我们枚举其祖先节点的前8位i,就可以通过f[i][b]得到该点的最优状态值。
然后我们再更新f[a][0~255]
【时间复杂度&&优化】
O(暴力) -> O(n * 2^8)
*/