【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個整數V 1, V 2, …, V N,其中第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≤V i≤10 9, 保證輸入的是一棵合法的樹.

Solution

這道題,先考慮弱化版本..ios

若是$K=0$不考慮通過關鍵點的數量,很是簡單。ide

能夠直接從根dfs一遍獲得到全部節點的xor和,而後所有插入Trie中,再枚舉每一個點貪心在Trie上跑便可,複雜度$O(NlogN)$。測試

可是這個題須要限制通過節點數,就必須點分治+Trie貪心,時間複雜度$O(Nlog^{2}N)$。spa

最普通的想法就是對於通過不一樣的關鍵點的路徑xor和分別建一棵Trie樹,而後查詢的時候查詢便可,可是$K$上限過大。blog

因此考慮維護一棵Trie樹,在每一個節點上維護一下通過的關鍵點數,在貪心統計答案的時候處理一下便可。ip

這裏有一個問題,就是處理一棵子樹的時候,最頂部的節點會重複計算,因此能夠考慮先不計算它的影響,在統計答案的時候再計算回來。內存

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;
}
相關文章
相關標籤/搜索