【HDU5735 2016 Multi-University Training Contest 2B】【暴力做法 + 折半法】Born Slippy 祖先鏈的最大運算權值

snowy_smile發表於2016-07-28

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.
 

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.
 

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)

*/


相關文章