Tsinsen A1486. 树(树分治+字典树)

传送门:Tsinsen A1486. 树


题意:给你一棵树(n<=1e5),每个点有一个权值和喜欢不喜欢之分,找出一条路径至少包含K个喜欢的点,而且异或和最大


思路:
树分治,同时维护字典树
字典树每个点维护一个喜欢的点数的最大值
时间复杂度:nlogn*30

#include<bits/stdc++.h>
using namespace std;
const int N=101000;
typedef pair<int,int> PI;
struct Edge{
    int to,next;
}e[N*2];
int tot,head[N];

void addedge(int from,int to){
    e[tot]=(Edge){to,head[from]};
    head[from]=tot++;
}

void init(){
    memset(head,-1,sizeof(head));
    tot=0;
}

int Count,size[N],f[N],root,K,ans,a[N],color[N];
bool Del[N];

void getroot(int u,int fa){
    size[u]=1,f[u]=0;
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(v==fa||Del[v])   continue;
        getroot(v,u);
        f[u]=max(f[u],size[v]);
        size[u]+=size[v];
    }
    f[u]=max(f[u],Count-size[u]);
    if(f[u]<f[root])    root=u;
}

struct Trie{
    int ch[N*31][2],val[N*31];
    int sz;
    void clear(){
        sz=1;
        memset(ch[0],0,sizeof(ch[0]));
    }

    void insert(int x,int dep){
        int u=0;
        for(int i=30;i>=0;i--){
            int c=(x&(1<<i))==0 ? 0:1;
            if(!ch[u][c]){
                memset(ch[sz],0,sizeof(ch[sz]));
                val[sz]=0;
                ch[u][c]=sz++;
            }
            u=ch[u][c];
            val[u]=max(val[u],dep);
        }
    }

    int find(int x,int dep){//查找的值,查找的深度
        int ans=0,u=0;
        for(int i=30;i>=0;i--){
            int c=(x&(1<<i))==0 ? 0:1;
            if(ch[u][c^1]&&val[ ch[u][c^1] ]>=dep)
                ans+=(1<<i),u=ch[u][c^1];
            else if(ch[u][c]&&val[ ch[u][c] ]>=dep)
                u=ch[u][c];
            else return -1;
        }
        return ans;
    }
}trie;

vector<PI>tmp;

void getdeep(int u,int pre,int dep,int pre_value,int value){
    size[u]=1;
    ans=max(ans,trie.find(pre_value^value,K-dep));
    tmp.push_back((PI){pre_value,dep});
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]&&v!=pre)
            getdeep(v,u,dep+color[v],pre_value^a[v],value),size[u]+=size[v];
    }
}

void work(int u){
    Del[u]=true;
    trie.clear();
    K-=color[u];
    if(K<=0)    ans=max(ans,a[u]);
    trie.insert(0,0);
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]){
            tmp.clear();
            getdeep(v,u,color[v],a[v],a[u]);
            for(int j=0;j<tmp.size();j++)
                trie.insert(tmp[j].first,tmp[j].second);
        }
    }
    //printf("%d %d\n",u,ans);
    K+=color[u];
    for(int i=head[u];i!=-1;i=e[i].next){
        int v=e[i].to;
        if(!Del[v]){
            Count=f[0]=size[v];
            getroot(v,root=0);
            work(root);
        }
    }
}

int main(){
    int n,u,v;
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)   scanf("%d",&color[i]);
    for(int i=1;i<=n;i++)   scanf("%d",&a[i]);
    init();
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        addedge(u,v),addedge(v,u);
    }
    Count=f[0]=n,ans=-1;
    memset(Del,false,sizeof(Del));
    getroot(1,root=0);
    work(root);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值