[codeforces840E] In a Trap

题目大意

有一个n个节点的有根树,边权为1,点权给定,第i个点的点权是ai。q次询问,每次给定u,v,并保证u是v的祖先。问对于u到v路径上的所有i(包括u,v),最大的ai xor dist(i,v)。其中dist(i,v)是i到v的距离

n≤50000 0≤ai≤n q≤150000

分析

这题的做法很有趣。
我们把v到u的路径每256个节点分成一段(最后一段可能不够256个,可以单独枚举更新答案),容易发现,对于同一段,dist在2进制下从低到高第8位以后都是相同的,最低8位则是从0开始到255。
那么可以预处理每个点向上256个节点对于2进制下高位的trie,并以此维护只考虑最低8位时点权异或对应距离的最大值。例如枚举到点i,它上面一个节点为j,那么把它扔到trie之后插入的值是(dep[i]-dep[j]^a[j])&255
查询时也是一样,每跳256个节点就在trie上贪心走到高位异或最大的节点,然后低位直接由上面插入的最大值得到。
由于每次都跳大约 n 次,应该预处理答案来提高运行效率

时间复杂度 O(nnlogn+qn)

#include <cstdio>
#include <cstring>
#include <algorithm>

#define max(a,b) ((a)>(b)?(a):(b))

using namespace std;

const int N=5e4+5,st=256;

typedef long long LL;

int n,q,fa[N],fa256[N],h[N],tot,e[N<<1],nxt[N<<1],mx[N][st],cnt[N][st],Ans[N][st],ans,val[N],dep[N];

char c;

int read()
{
    int x=0,sig=1;
    for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
    for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
    return x*sig;
}

void Add(int x,int y)
{
    e[++tot]=y; nxt[tot]=h[x]; h[x]=tot;
}

void dfs(int x)
{
    dep[x]=dep[fa[x]]+1;
    int i;
    if (dep[x]>=st)
    {
        int l,r,mid,s;
        for (i=x;dep[x]-dep[i]<st;i=fa[i])
        {
            mx[x][val[i]>>8]=max(mx[x][val[i]>>8],(dep[x]-dep[i]^val[i])&255);
            cnt[x][val[i]>>8]++;
        }
        fa256[x]=i;
        for (i=1;i<st;i++) cnt[x][i]+=cnt[x][i-1];
        for (i=0;i<=(n-1)/st;i++)
        {
            for (l=s=0,r=st,mid=r>>1;l<r-1;mid=l+r>>1)
            {
                if ((s^i)<mid)
                {
                    if (cnt[x][r-1]>cnt[x][mid-1]) s|=r-mid,l=mid;else r=mid;
                }else
                {
                    if (cnt[x][mid-1]>((l)?cnt[x][l-1]:0)) s|=r-mid,r=mid;else l=mid;
                }
            }
            Ans[x][i]=(s<<8)|mx[x][l];
        }
    }
    for (i=h[x];i;i=nxt[i]) if (e[i]!=fa[x])
    {
        fa[e[i]]=x; dfs(e[i]);
    }
}

int main()
{
    n=read(); q=read();
    for (int i=1;i<=n;i++) val[i]=read();
    for (int x,y,i=1;i<n;i++)
    {
        x=read(); y=read(); Add(x,y); Add(y,x);
    }
    for (dfs(1);q--;)
    {
        int u=read(),v=read(),dis;
        for (dis=ans=0;dep[v]-dep[u]>=st;v=fa256[v],dis++) ans=max(ans,Ans[v][dis]);
        for (dis<<=8;v!=fa[u];v=fa[v],dis++) ans=max(ans,dis^val[v]);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值