• 【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
    ",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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    集合(二)
    集合(一)
    面向对象(三)
    面向对象(二)
    面向对象(一)
    关键字
    java基础
    android开发_LogCat的使用
    linux基础之CentOS7新特性
    shell编程(二)之算术运算
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/6322330.html
Copyright © 2020-2023  润新知