1. 程式人生 > >bzoj 2588 Count on a tree 解題報告

bzoj 2588 Count on a tree 解題報告

怎麽 輸出 for 報告 work top code sta rst

Count on a tree

題目描述

給定一棵\(N\)個節點的樹,每個點有一個權值,對於\(M\)個詢問\((u,v,k)\),你需要回答\(u\) \(xor\) \(lastans\)\(v\)這兩個節點間第\(K\)小的點權。其中\(lastans\)是上一個詢問的答案,初始為\(0\),即第一個詢問的u是明文。

輸入輸出格式

輸入格式:

第一行兩個整數\(N,M\)

第二行有\(N\)個整數,其中第\(i\)個整數表示點\(i\)的權值。

後面\(N-1\)行每行兩個整數\((x,y)\),表示點\(x\)到點\(y\)有一條邊。

最後\(M\)行每行兩個整數\((u,v,k)\)

,表示一組詢問。

輸出格式:

\(M\)行,表示每個詢問的答案。


一看是無修改的第\(k\)值查詢,我們可以用可持久化降維。

就是把序列上的第\(k\)值擴展到了樹上。

我們考慮一條樹上路徑可以被怎麽表示

這樣類比,假設樹上每個點有點權,則樹上路徑點權之和可以被樹的前綴和數組這樣表示
\(len(u,v)=dis[u]+dis[v]-dis[lca(u,v)]-dis[father(lca[u,v])]\)

然而前綴和其實就是一維的可持久化,我們把\(dis\)數組類比成主席樹加加減減就好了


Code:

#include <cstdio>
#include <algorithm>
#define ls ch[now][0]
#define rs ch[now][1]
const int N=100010;
int Next[N<<1],to[N<<1],head[N],cnt;
void add(int u,int v)
{
    Next[++cnt]=head[u];to[cnt]=v;head[u]=cnt;
}
int sum[N*25],ch[N*25][2],tot,n,m,n_;
void updata(int now)
{
    sum[now]=sum[ls]+sum[rs];
}
int rebuild(int las,int l,int r,int pos)
{
    int now=++tot;
    if(l==r)
    {
        sum[now]=sum[las]+1;
        return now;
    }
    int mid=l+r>>1;
    if(pos<=mid)
    {
        ls=rebuild(ch[las][0],l,mid,pos);
        rs=ch[las][1];
    }
    else
    {
        ls=ch[las][0];
        rs=rebuild(ch[las][1],mid+1,r,pos);
    }
    updata(now);
    return now;
}
int ha[N],loc[N],root[N];
int query(int u,int v,int lca,int lcaf,int l,int r,int k)
{
    if(l==r) return ha[l];
    int s=sum[ch[u][0]]+sum[ch[v][0]]-sum[ch[lca][0]]-sum[ch[lcaf][0]];
    int mid=l+r>>1;
    if(k<=s) return query(ch[u][0],ch[v][0],ch[lca][0],ch[lcaf][0],l,mid,k);
    else return query(ch[u][1],ch[v][1],ch[lca][1],ch[lcaf][1],mid+1,r,k-s);
}
int top[N],dfn[N],f[N],dep[N],ws[N],siz[N],time;
void dfs1(int now)
{
    root[now]=rebuild(root[f[now]],1,n,loc[now]);
    siz[now]++;
    for(int i=head[now];i;i=Next[i])
    {
        int v=to[i];
        if(v!=f[now])
        {
            f[v]=now;
            dep[v]=dep[now]+1;
            dfs1(v);
            siz[now]+=siz[v];
            if(siz[ws[now]]<siz[v])
                ws[now]=v;
        }
    }
}
void dfs2(int now,int anc)
{
    dfn[now]=++time;
    top[now]=anc;
    if(ws[now]) dfs2(ws[now],anc);
    for(int i=head[now];i;i=Next[i])
        if(!dfn[to[i]])
            dfs2(to[i],to[i]);
}
int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
            x=f[top[x]];
        else
            y=f[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
std::pair <int,int > node[N];
void init()
{
    scanf("%d%d",&n_,&m);
    for(int d,i=1;i<=n_;i++)
    {
        scanf("%d",&d);
        node[i]=std::make_pair(d,i);
    }
    std::sort(node+1,node+1+n_);
    for(int i=1;i<=n_;i++)
    {
        if(node[i].first!=node[i-1].first) n++;
        ha[n]=node[i].first;
        loc[node[i].second]=n;
    }
    for(int u,v,i=1;i<n_;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v),add(v,u);
    }
    dfs1(1);
    dfs2(1,1);
}
void work()
{
    for(int u,v,lca,k,lastans=0,i=1;i<=m;i++)
    {
        scanf("%d%d%d",&u,&v,&k);
        u^=lastans;
        lca=LCA(u,v);
        printf("%d\n",lastans=query(root[u],root[v],root[lca],root[f[lca]],1,n,k));
    }
}
int main()
{
    init();
    work();
    return 0;
}

2018.7.31

bzoj 2588 Count on a tree 解題報告