【01trie】【启发式合并】P6072 『MdOI R1』Path

题意

每个点有点权,求两条没有交点的路径,使得两条路径的异或和的和最大

n ≤ 3 e 4 时 限 : 3.5 s n \le 3e4 \\ 时限:3.5s n3e43.5s

分析

给每个点求出 i n [ i ]   o u t [ i ] in[i] \ out[i] in[i] out[i]表示子树内的最大异或路径和子树外的最大异或路径

in只需要给树dfs时,给Trie进行启发式合并即可两只log求出

对于求out,我们找出全局最大异或路径A-B的异或和为ans,考虑只有A->rt 和 B->rt 这两条路径上的点的out不是ans

我们要求两条链上的out,依次加入除了子节点子树外的部分到trie即可

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=3e4+5;
int n;
vector <PII> G[maxn];
struct Trie
{
    int ch[2];
    int gs;
}tr[maxn<<7];
int rt[maxn],a[maxn],tot;
int in[maxn],out[maxn];
int A,B,p,q;
void insert(int &p,int val)
{
    if(!p) p=++tot; 
    int now=p;
    tr[now].gs++;
    for(int i=30;i>=0;i--)
    {
        int dig=val>>i&1;
        if(!tr[now].ch[dig]) tr[now].ch[dig]=++tot;
        now=tr[now].ch[dig];
        tr[now].gs++;
    }
}
int query(int now,int val)
{
    int res=0;
    for(int i=30;i>=0;i--)
    {
        int dig=val>>i&1;
        if(tr[tr[now].ch[!dig]].gs) now=tr[now].ch[!dig],res|=1<<i;
        else now=tr[now].ch[dig];
    }
    return res;
}
void merge(int x,int p,int val,int wei,int &v)
{
    if(!x) return;
    if(wei==-1)
    {
        v=max(v,query(rt[p],val));
        insert(rt[p],val);
        return;
    }
    merge(tr[x].ch[0],p,val,wei-1,v);
    merge(tr[x].ch[1],p,val|1<<wei,wei-1,v);
}
int father[maxn],dfn[maxn],dfs_time;
void dfs(int u,int fa)
{
    father[u]=fa; dfn[++dfs_time]=u;
    insert(rt[u],a[u]);
    for(auto to:G[u])
    {
        int v=to.first;
        if(v==fa) continue;
        a[v]=a[u]^to.second;
        dfs(v,u);
        if(tr[rt[v]].gs>tr[rt[u]].gs) swap(rt[u],rt[v]);
        merge(rt[v],u,0,30,in[u]);
        in[u]=max(in[u],in[v]);
    }
}
int tag[maxn],bel[maxn];
vector <int> v[maxn];
void calc(int x)
{
    for(int i=1;i<=tot;i++) tr[i].gs=0;
    for(int i=x;i;i=father[i]) tag[i]=1;
    int now=0;
    for(int k=1,i;k<=n;k++,v[i].clear(),v[bel[i]].push_back(a[i]))
    {
        if(tag[i=dfn[k]]) bel[i]=i;
        else bel[i]=bel[father[i]];
    }
    for(int i,k=1;k<=n;k++)
        if(tag[i=dfn[k]])
        {
            for(int w:v[father[i]])
                now=max(now,query(rt[0],w)),insert(rt[0],w);
            out[i]=max(out[i],now);
            tag[i]=0;
        }
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    scanf("%d",&n);
    int x,y,z;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        G[x].push_back(make_pair(y,z));
        G[y].push_back(make_pair(x,z));
    }
    dfs(1,0);
    // for(int i=1;i<=n;i++) printf("%d ",in[i]);
    for(int i=1;i<=tot;i++) tr[i].gs=0;
    for(int i=1;i<=n;i++)
    {
        int val=query(rt[0],a[i]);
        insert(rt[0],a[i]); out[i]=-1;
        if((A^B)<val)
        {
            A=a[i];
            B=a[i]^val;
            p=i;
        }
    }
    for(int i=1;i<=n;i++) if(B==a[i]) q=i;
    calc(p); calc(q);
    for(int i=1;i<=n;i++) if(out[i]==-1) out[i]=A^B;
    int ans=0;
    for(int i=2;i<=n;i++) ans=max(ans,in[i]+out[i]);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值