【Tsinsen-A1486】树(王康宁) 点分治 + Trie

A1486. 树(王康宁)

时间限制:1.0s   内存限制:512.0MB

总提交次数:455   AC次数:97   平均分:52.62

查看未格式化的试题   提交   试题讨论

试题来源

  2013中国国家集训队第二次作业

问题描述

  给出一棵N个点的树,每个点有各自的权值,小A想选出一条简单路径,使得这条路径上的点的权值的异或和最大。另外,小A有一些喜欢的点,他希望在这条路径上经过至少K个自己喜欢的点。

输入格式

  第一行包括两个整数N, K,分别表示树的点数和路径上至少包含的小A喜欢的点的数量。
  接下来一行N个整数,每个数均为0或1,小A喜欢第i个点当且仅当这一行的第i个数是1。
  接下来一行N个整数V1, V2, …, VN,其中第i个数表示第i个点的权值。
  最后N-1行,每行两个整数u, v,表示树的一条边。

输出格式

  如果不存在这样的简单路径,输出-1。否则输出最大的异或和。

样例输入

3 1
1 1 1
0 4 7
1 2
2 3

样例输出

7

样例输入

3 2
1 0 1
3 5 6
1 2
2 3

样例输出

0

样例输入

4 4
1 1 1 1
10 10 10 10
1 2
1 3
1 4

样例输出

-1

数据规模和约定

测试点标号 N的范围 K的范围 Vi的范围 其它特点
1 N=2 K=0
2 N≤10
3 N≤1000 这棵树是一条链
4 N≤1000 K=0 这棵树是一条链
5 N≤4000
6 N≤30000 K=0 Vi≤7
7 N≤40000 K=0
8 N≤40000 K=0
9 N≤50000 K=0 这棵树是一条链
10 N≤50000 K=0
11 N≤60000 Vi≤7
12 N≤60000 这棵树是一条链
13 N≤70000 Vi≤107
14 N≤70000 Vi≤107
15 N≤80000
16 N≤80000
17 N≤90000
18 N≤90000
19 N≤100000
20 N≤100000

  对于100%的数据,1≤N≤100000, 0≤K≤N, 0≤Vi≤109, 保证输入的是一棵合法的树.

Solution

这道题,先考虑弱化版本..

如果$K=0$不考虑经过关键点的数量,非常简单。

可以直接从根dfs一遍得到到所有节点的xor和,然后全部插入Trie中,再枚举每个点贪心在Trie上跑即可,复杂度$O(NlogN)$。

但是这个题需要限制经过节点数,就必须点分治+Trie贪心,时间复杂度$O(Nlog^{2}N)$。

最普通的想法就是对于经过不同的关键点的路径xor和分别建一棵Trie树,然后查询的时候查询即可,但是$K$上限过大。

所以考虑维护一棵Trie树,在每个节点上维护一下经过的关键点数,在贪心统计答案的时候处理一下即可。

这里有一个问题,就是处理一棵子树的时候,最顶部的节点会重复计算,所以可以考虑先不计算它的影响,在统计答案的时候再计算回来。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
	while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
	return x*f;
}
#define MAXN 100010

int N,K,lov[MAXN],c[MAXN],ans=-1;

struct EdgeNode{
	int next,to;
}edge[MAXN<<1];
int head[MAXN],cnt=1;
inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}

namespace Trie{
	int son[MAXN][2],sz,d[MAXN];
	int s[32];
	inline void Calc(int x) {memset(s,0,sizeof(s)); for (int t=31; t>=0; t--) s[t]=(x>>t)&1;}
	inline void Clear() {son[1][0]=son[1][1]=0; sz=1;}
	inline void Insert(int x,int dep)
	{
		int now=1;
		Calc(x);
		for (int i=31; i>=0; i--)
			if (son[now][s[i]]) now=son[now][s[i]],d[now]=max(d[now],dep);
				else son[now][s[i]]=++sz,now=sz,son[now][1]=son[now][0]=0,d[now]=dep;
	}
	inline int Query(int x,int dep)
	{
		int now=1,mx=0;
		Calc(x);
		for (int i=31; i>=0; i--) {
			if (son[now][s[i]^1] && d[son[now][s[i]^1]]>=dep) now=son[now][s[i]^1],mx|=(1<<i);
				else if (son[now][s[i]] && d[son[now][s[i]]]>=dep) now=son[now][s[i]];
					else return -1;
		}
		return mx;
	}
}using namespace Trie;

namespace TreeDivide{
	int size[MAXN],mx[MAXN],Sz,root;
	bool visit[MAXN];
	struct Node{
		int x,y;
		Node (int X=0,int Y=0) {x=X,y=Y;}
	}stack[MAXN];
	int top;
	inline void Getroot(int now,int last)
	{
		size[now]=1,mx[now]=0;
		for (int i=head[now]; i; i=edge[i].next)
			if (edge[i].to!=last && !visit[edge[i].to]) {
				Getroot(edge[i].to,now);
				size[now]+=size[edge[i].to];
				mx[now]=max(mx[now],size[edge[i].to]);
			}
		mx[now]=max(mx[now],Sz-size[now]);
		if (mx[now]<mx[root]) root=now;
	}
	inline void DFS(int now,int last,int dep,int val,int fav)
	{
		ans=max(ans,Query(val^fav,K-dep));
		stack[++top]=Node(val,dep);
		for (int i=head[now]; i; i=edge[i].next)
			if (edge[i].to!=last && !visit[edge[i].to]) {
				DFS(edge[i].to,now,dep+lov[edge[i].to],val^c[edge[i].to],fav);
			}
	}
	inline void Divide(int now)
	{
//		printf("root=%d\n",now);
		visit[now]=1;
		Trie::Clear();
		K-=lov[now];
		if (K<=0) ans=max(ans,c[now]);
		for (int i=head[now]; i; i=edge[i].next)
			if (!visit[edge[i].to]) {
				top=0;
				DFS(edge[i].to,now,lov[edge[i].to],c[edge[i].to],c[now]);
				for (int j=1; j<=top; j++)
					Trie::Insert(stack[j].x,stack[j].y);
			}
		K+=lov[now];
		for (int i=head[now]; i; i=edge[i].next)
			if (!visit[edge[i].to]) {
				root=0;
				Sz=size[edge[i].to];
				Getroot(edge[i].to,now);
				Divide(root);
			}
	}
}using namespace TreeDivide;

int main()
{
	N=read(),K=read();
	for (int i=1; i<=N; i++) lov[i]=read();
	for (int i=1; i<=N; i++) c[i]=read();
	for (int i=1,x,y; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y);
	mx[root=0]=Sz=N;
	Getroot(1,0);
	Divide(root);
	printf("%d\n",ans);
	return 0;
}
时间: 2024-10-17 05:20:19

【Tsinsen-A1486】树(王康宁) 点分治 + Trie的相关文章

tsinsen A1486. 树(王康宁) 边分治+字典树

不知为何,这个代码只能得95分 放一下傻逼代码... #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int n,K; int nn2=1,nn=1,nod; #define N 1000000 #define ed(x) (x>>1) #define ab(x) ((x)>0?(x):-(x)) i

Tsinsen A1486. 树(王康宁)

Description 一棵树,问至少有 \(k\) 个黑点的路径最大异或和. Sol 点分治. 用点分治找重心控制树高就不说了,主要是对答案的统计的地方. 将所有路径按点的个数排序. 可以发现当左端点递增的时候右端点单调递减,时刻满足Trie树里的所有元素都是合法的即可,不断把右端点丢进去,用左端点统计答案. 主要跨越根的时候根的贡献计算了两次,需要删掉一次. 对于需要满足不是一颗子树,可以将Trie树上的节点打一个标记,表示这个节点及其子节点都是在某子树下的路径,子树个数大于1的时候这个标记

Codeforcecs 459D Pashmak and Parmida&#39;s problem 树状数组 OR 分治

http://codeforces.com/contest/459/problem/D 题意:定义:f(l,r,x) [l,r]内x的个数 ,n个数,a[i] n<=1e5,a[i]<=1e9 问i<j时满足f(1,i,a[i]) > f(j,n,a[j]) 的(i,j)对数? 预处理出后缀f(j) 插入到BIT中 枚举i,查询mp[a[i]]-1,后更新BIT即可. #include <bits/stdc++.h> using namespace std; typed

A1486. 树(王康宁)

题目:http://www.tsinsen.com/A1486 题解: 其实看到和路径有关的就应该想到点分治. 我们找出重心之后遍历每一棵子树得到它的 { x=经过特殊点的个数,y=到rt的异或和} 然后我们按x排序,维护两个头尾指针不断把满足条件的加入trie,然后把左边的放进trie里查询. 但是还有一个问题,所取的两个点不能位于同一棵子树!!! 我yy了一个做法.我们在用三元组来记录{ x=经过特殊点的个数,y=到rt的异或和,ch=所属子树} 然后往trie里插的时候,每条边保留两个ch

【xsy1122】 路径 点分治+trie

题目大意:给你一棵n个点的树,树边上有边权,对于每一个点,你要求出经过该点的所有的路径中,路径异或和最大的值. 数据范围:$n≤10^5$,边权$≤10^9$. 我们考虑枚举每一条路径,显然这个是会T的,于是我们用点分治来实现这个过程. 对于一棵以$x$为根的子树,假设它有$k$个儿子,编号$v1....k$. 我们维护一棵$trie$树,记录所有从根到以$v1,v2.....vp$为跟的子树中的路径长度. 对于从跟到第$p+1$个儿子为跟的树中的每一条路径,我们都丢到$trie$树中查询与最大

Tsinsen A1493 城市规划(DP + CDQ分治 + NTT)

题目 Source http://www.tsinsen.com/A1493 Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多

【BZOJ-3648】寝室管理 环套树 + 树状数组 + 点分治

3648: 寝室管理 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 239  Solved: 106[Submit][Status][Discuss] Description T64有一个好朋友,叫T128.T128是寄宿生,并且最近被老师叫过去当宿管了.宿管可不是一件很好做的工作,碰巧T128有一个工作上的问题想请T64帮忙解决.  T128的寝室条件不是很好,所以没有很多钱来装修.礼间寝室仅由n-1条双向道路连接,而且任意两间寝室之间都可以互

【BZOJ-2229】最小割 最小割树(最大流+分治)

2229: [Zjoi2011]最小割 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1565  Solved: 560[Submit][Status][Discuss] Description 小白在图论课上学到了一个新的概念——最小割,下课后小白在笔记本上写下了如下这段话: “对于一个图,某个对图中结点的划分将图中所有结点分成两个部分,如果结点s,t不在同一个部分中,则称这个划分是关于s,t的割. 对于带权图来说,将所有顶点处在不同部分的边的

Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB 总提交次数:196   AC次数:65   平均分:58.62 将本题分享到: 查看未格式化的试题   提交   试题讨论 试题来源 2013中国国家集训队第二次作业 问题描述 给定一棵N个节点的树,每个点有一个权值,有M个询问(a,b,c)若a 为1,回答b到c路径上的最小权值,若a为2,回答b到c路径上的最大权值,若a为3,回答b到c路径上的所有权值的