2016 UESTC Training for Data Structures L - 郭大侠与苦恼 CDOJ 1284 map+启发式合并

L - 郭大侠与苦恼

题目简单,就是给你一棵树,然后问异或值为0的简单路径有多少条

好难,好麻烦,想了好久才想明白

dfs的顺序来map+启发式合并,

后来想找个题的大概框架和树形01背包相似,都是用dfs来实现的,只不过树形01背包dfs后的过程是背包的合并,这个是map的合并,还是启发式合并(就是把size小的合并到size大的上),

然后我们这里维护的是pre[u],就是从根节点到u的简单路径异或和,

uv的简单路径异或和就是pre[u]^pre[v]^lca(u,v)

对于一个dfs,根节点的map维护之前遍历过的子树的每个异或值的个数,然后当前的儿子节点继续往下dfs,在回溯到这里的时候,这个节点上的map就已经维护好了以它为根的各个异或值的个数,然后我们就统计就好了,选择一个size小的进行遍历,对于每个值x,查询x^nownow代表当前的这个根节点,就是他们的最近公共祖先)在另一个map中出现的次数,然后查询完了之后,合并,如果根节点的size比儿子节点的size大,就先交换一下,再合并,就好了

总之就是dfs的框架思路和树形01背包的框架思路是一样的,我感觉,就是在dfs的的过程中维护它就好,维护一个之前已经遍历完的,和这个节点dfs之后回溯回来的,把这两个合并到一起就行


代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
#define ll long long
#define maxn 100005
int n, idx[maxn];
bool vis[maxn];
vector<int> tree[maxn];
map<int, int> XOR[maxn];//记录以该节点为根的子树的各个异或值的计数
ll ans = 0;
void dfs_print(int now)
{
	vis[now] = 1;
	for (int i = 0; i < tree[now].size(); ++i)
	{
		if (vis[tree[now][i]])
			continue;
		printf("%d %d\n", now, tree[now][i]);
		dfs_print(tree[now][i]);
	}
}
void dfs(int now, int value)
{
	vis[now] = 1;
	++XOR[now][value];
	//printf("every %d %d\n", now, value);
	for (int i = 0; i < tree[now].size(); ++i)
	{
		int tmp = tree[now][i];
		if (!vis[tmp])
		{
			dfs(tmp, value^tmp);
			if (XOR[now].size() < XOR[tmp].size())//统计当前子树和根节点及之前所有子树异或值相同的个数
			{
				for (map<int, int>::iterator itor = XOR[now].begin(); itor != XOR[now].end(); ++itor)
				{
					if (XOR[tmp].find(now ^ (itor->first)) != XOR[tmp].end())
						ans += (itor->second)*XOR[tmp][now ^ (itor->first)];
				}
				XOR[now].swap(XOR[tmp]);
			}
			else
			{
				for (map<int, int>::iterator itor = XOR[tmp].begin(); itor != XOR[tmp].end(); ++itor)
				{
					if (XOR[now].find(now ^ (itor->first)) != XOR[now].end())
						ans += (itor->second)*XOR[now][now ^ (itor->first)];
				}
			}
			//把子树并入到它的父节点中
			for (map<int, int>::iterator itor = XOR[tmp].begin(); itor != XOR[tmp].end(); ++itor)
			{
				XOR[now][itor->first] += itor->second;
			}
			/*for (map<int, int>::iterator itor = XOR[now].begin(); itor != XOR[now].end(); ++itor)
				printf("%d %d\n", itor->first, itor->second);
			printf("%d %d  ans %lld\n", now, tmp, ans);*/
		}
	}
}
int main()
{
	//freopen("input.txt", "r", stdin);
	scanf("%d", &n);
	int u, v;
	for (int i = 0; i < n - 1; ++i)
	{
		scanf("%d%d", &u, &v);
		tree[u].push_back(v);
		tree[v].push_back(u);
	}
	/*dfs_print(1);
	printf("ssssssssssss\n");
	memset(vis, 0, sizeof(vis));*/
	dfs(1, 1);
	printf("%lld\n", ans);
	//system("pause");
	//while (1);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值