【Tsinsen-A1486】树(王康宁) 点分治 + Trie
A1486. 树(王康宁)
试题来源
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,表示树的一条边。
接下来一行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
1 1 1
0 4 7
1 2
2 3
样例输出
7
样例输入
3 2
1 0 1
3 5 6
1 2
2 3
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 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 |
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; }
——It's a lonely path. Don't make it any lonelier than it has to be.