CodeForces 741D Arpa’s letter-marked tree and Mehrdad’s Dokhtar-kosh paths

Problem Link: CF 741D
Soution:
 注意到字母最大是v,即最多有22种字符,看到这个数据范围就想到利用状压。那么在这题怎么用上状压呢?想到一个性质:一个简单路径可以通过打乱变成回文串,当且仅当在这个路径上出现的次数为奇数的字符个数不超过一个。
 我们状压记录每个字符出现的奇偶性,偶数为0,奇数为1,那么对于以 x x x为根的子树中,记 s t a p sta_p stap为p点到x的路径状态, s t a q sta_q staq为q点到x的路径状态,那么显然这个总路径的状态就是 s t a p sta_p stap ^ s t a q sta_q staq,暂记为 r e t ret ret,显然路径合法的条件是: r e t ret ret中1的个数 ≤ 1 \leq 1 1
 知道了如何记录,先考虑暴力求解。
s u b t a s k 1 : subtask_1: subtask1:从根节点x开始搜索,记录所有路径的状态,如何找到这个状态是否和其他链异或起来有合法解?其实也简单,考虑到异或性质的特殊性,即x ^ y = z,则x ^ z = y.那么把当前的路径状态和所有的合法状态(显然只有23种)异或一下,结果暂记为 n o w now now,再查找是否有 n o w now now这个状态就行了。
s u b t a s k 2 : subtask_2: subtask2:如何得出答案?先求出所有节点的深度,记为 d e p i dep_i depi,那么对于上述的合法状态,答案就是 d e p p + d e p q − 2 ∗ d e p x dep_p + dep_q - 2 * dep_x depp+depq2depx,可以看出 d e p p dep_p depp d e p q dep_q depq越大越好,所以对于有相同状态的路径,只需要记录深度最深的那个就行了,这样复杂度也有保证。
 上述的暴力求解因为每次要清空状态数组重新记录,复杂度 O ( 23 ∗ n 2 ) O(23 * n^2) O(23n2),无法通过,想到树的特殊性,题目又是静态的没有修改,加入树上启发式合并,复杂度降为 O ( 23 ∗ n ∗ l o g 2 n ) O(23 * n * log_2n) O(23nlog2n),就可以通过啦。

代码得思考一下怎么写。
AC Code:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define endl "\n"
#define fi first
#define se second
#define db double
#define gcd __gcd
#define pb push_back
#define mp make_pair
#define lowbit(x) (x & (-x))
#define PII  pair 
#define all(x) x.begin(), x.end()
#define debug(x) cout << #x << " = " << x << endl
#define rep(i, a, b) for(__typeof(b) i = a; i <= (b); i++)
#define Rep(i, a, b) for(__typeof(a) i = a; i >= (b); i--)
#define FAST ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
template<class T> inline T qmin(T a, T b) { return a < b ? a : b; }
template<class T> inline T qmax(T a, T b) { return a > b ? a : b; }
typedef long long ll;
typedef unsigned long long ull;
const db eps = 1e-9;
const db PI = acos(-1);
const int inf = 0x3f3f3f3f;
const int mod = (int)1e9 + 7;
const int maxn = (int)5e5 + 5;//remember to modify it, No RE or MLE 
const ll INF = 0x3f3f3f3f3f3f3f3f;
using namespace std;

int n;
vector<PII> G[maxn];
int sz[maxn], Wson[maxn], dep[maxn];
int Son;
int ans[maxn], cnt[26];
int sta[1<<22], xor_sum[maxn];

void Max(int &a, int b){
	a = max(a, b);
}

int lg[30];
void init(){
	rep(i, 0, 25) lg[i] = 1 << i;
}

void dfs1(int u){
	sz[u] = 1;
	for(PII to : G[u]){
		int v = to.fi;
		xor_sum[v] = xor_sum[u] ^ to.se;
		dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
		if(sz[v] > sz[Wson[u]]) Wson[u] = v;
	}
}

void work1(int u, int root){
	if(sta[xor_sum[u]]) Max(ans[root], sta[xor_sum[u]] + dep[u] - 2 * dep[root]);
	rep(i, 0, 21) if(sta[xor_sum[u]^lg[i]]) Max(ans[root], sta[xor_sum[u]^lg[i]] + dep[u] - 2 * dep[root]);
	for(PII to : G[u]){
		int v = to.fi;
		work1(v, root);
	}
}

void work2(int u, int opt){
	if(opt == 1) Max(sta[xor_sum[u]], dep[u]);
	else sta[xor_sum[u]] = 0;
	for(PII to : G[u]){
		int v = to.fi;
		work2(v, opt);
	}
}

void dfs2(int u, int opt){
	for(PII to : G[u]){
		int v = to.fi;
		if(v != Wson[u]) dfs2(v, 1), Max(ans[u], ans[v]);
	}
	if(Wson[u]) dfs2(Wson[u], 0), Max(ans[u], ans[Wson[u]]);
	Max(sta[xor_sum[u]], dep[u]);
	Max(ans[u], sta[xor_sum[u]] - dep[u]);
	rep(i, 0, 21) if(sta[xor_sum[u]^lg[i]]) Max(ans[u], sta[xor_sum[u]^lg[i]] - dep[u]);
	for(PII to : G[u]){
		int v = to.fi;
		if(v == Wson[u]) continue;
		work1(v, u);
		work2(v, 1);
	}
	if(opt) work2(u, 0);
}


int main()
{
	init();
	scanf("%d", &n);
	rep(i, 2, n){
		int x; char c; scanf("%d %c", &x, &c);
		G[x].pb(mp(i, lg[c-'a']));
	}
	dep[1] = 1;
	dfs1(1);
	dfs2(1, 0);
	rep(i, 1, n) printf("%d%c", ans[i], i == n ? '\n' : ' ');
	return 0;
}

你可能感兴趣的:(杂题解)