codeforces 893F 主席樹||線段樹合併

題意:有一顆樹,樹上每個點有給定點權,有m次詢問,每次詢問點x的所有子樹中,與x的距離小於等於k的所有點的點權最小值是多少。題目要求強制在線。

思路:

主席樹解法:按照dfs序在樹上建立主席樹,詢問是查詢x節點的管轄的那段區間,屬於經典操作,問題在於如何控制距離小於等於k,於是我們可以按照點的深度來建主席樹,雖然最小值問題不滿足前綴相減的性質,但實際上我們並不需要減掉1到dep[x]這段區間,因爲x節點dfs序管轄範圍的限制,前面的點不會對答案產生影響,所以直接查詢1到dep[x]+k這段區間即可。

線段樹合併解法:以點的深度爲基準來建線段樹,對每個點都維護一顆線段樹,再進行線段樹合併即可。要注意的一點是由於詢問的是最小值,而數組的初始值默認爲0,所以在線段樹合併時要開新點來合併,而不能直接利用已有的點,並且新點對應的最小值爲要合併的兩個點的最小值,而不能直接取左右兒子的最小值。

以前一直覺得線段樹合併和主席樹非常類似,但是又說不出個所以然,這道題很好的展現了線段樹合併與主席樹的異同點。

 

主席樹代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[maxn];
vector<int> g[maxn];
int root[maxn];
int dep[maxn];
int pl[maxn],pr[maxn];
int tree[maxn*40],ls[maxn*40],rs[maxn*40];
int cnt;
int tot;
void pushup(int k)
{
    tree[k]=min(tree[ls[k]],tree[rs[k]]);
}
void build(int &x,int l,int r)
{
    x=++cnt;
    tree[x]=inf;
    if(l==r) return ;
    int mid=(l+r)/2;
    build(ls[x],l,mid);
    build(rs[x],mid+1,r);
}
void insert(int &x,int pre,int l,int r,int pos,int c)
{
    if(!x) x=++cnt;
    if(l==r)
    {
        tree[x]=c;
        return ;
    }
    int mid=(l+r)/2;
    if(pos<=mid) insert(ls[x],ls[pre],l,mid,pos,c),rs[x]=rs[pre];
    else insert(rs[x],rs[pre],mid+1,r,pos,c),ls[x]=ls[pre];
    pushup(x);
}
int query(int L,int R,int l,int r,int x)
{
    if(L<=l&&r<=R) return tree[x];
    int mid=(l+r)/2;
    int mmin=inf;
    if(L<=mid) mmin=min(mmin,query(L,R,l,mid,ls[x]));
    if(R>mid) mmin=min(mmin,query(L,R,mid+1,r,rs[x]));
    return mmin;
}
void dfs(int x,int pre)
{
    dep[x]=dep[pre]+1;
    pl[x]=++tot;
    for(int i=0;i<g[x].size();i++)
    {
        int now=g[x][i];
        if(now==pre) continue;
        dfs(now,x);
    }
    pr[x]=tot;
}
int vis[maxn],mp[maxn];
void bfs(int x,int n)
{
    queue<int> q;
    q.push(x);
    int pre=0;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=1;
        insert(root[pre+1],root[pre],1,n,pl[now],a[now]);
        mp[dep[now]]=++pre;
        for(int i=0;i<g[now].size();i++)
        {
            if(vis[g[now][i]]) continue;
            q.push(g[now][i]);
        }
    }
}
int main()
{
    int n,r;
    scanf("%d%d",&n,&r);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(r,0);
    int maxdep=0;
    for(int i=1;i<=n;i++)
        maxdep=max(maxdep,dep[i]);
    build(root[0],1,n);
    bfs(r,n);
    int m;
    scanf("%d",&m);
    int x,k,last=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&k);
        x=(x+last)%n+1,k=(k+last)%n;
        last=query(pl[x],pr[x],1,n,root[mp[min(dep[x]+k,maxdep)]]);
        printf("%d\n",last);
    }
    return 0;
}

 

線段樹合併代碼:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[maxn];
vector<int> g[maxn];
int root[maxn];
int dep[maxn];
int tree[maxn*40],ls[maxn*40],rs[maxn*40];
int cnt;
void pushup(int k)
{
    tree[k]=min(tree[ls[k]],tree[rs[k]]);
}
void update(int p,int c,int l,int r,int &x)
{
    if(!x)
        x=++cnt;
    tree[x]=c;
    if(l==r)
    {
        return ;
    }
    int mid=(l+r)/2;
    if(p<=mid) update(p,c,l,mid,ls[x]);
    else update(p,c,mid+1,r,rs[x]);
}
int merge(int x,int y)
{
    if(!x) return y;
    if(!y) return x;
    int now=++cnt;
    ls[now]=merge(ls[x],ls[y]);
    rs[now]=merge(rs[x],rs[y]);
    tree[now]=min(tree[x],tree[y]);
    return now;
}
void dfs1(int x,int pre)
{
    dep[x]=dep[pre]+1;
    for(int i=0;i<g[x].size();i++)
    {
        int now=g[x][i];
        if(now==pre) continue;
        dfs1(now,x);
    }
}
void dfs2(int x,int pre,int n)
{
    update(dep[x],a[x],1,n,root[x]);
    for(int i=0;i<g[x].size();i++)
    {
        int now=g[x][i];
        if(now==pre) continue;
        dfs2(now,x,n);
        root[x]=merge(root[x],root[now]);
    }
}
int query(int L,int R,int l,int r,int k)
{
    if(!k) return inf;
    if(L<=l&&r<=R)
        return tree[k];
    int mid=(l+r)/2;
    int mmin=inf;
    if(L<=mid)
        mmin=min(mmin,query(L,R,l,mid,ls[k]));
    if(R>mid)
        mmin=min(mmin,query(L,R,mid+1,r,rs[k]));
    return mmin;
}
int main()
{
    int n,r;
    scanf("%d%d",&n,&r);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs1(r,0);
    int maxdep=0;
    for(int i=1;i<=n;i++)
        maxdep=max(maxdep,dep[i]);
    dfs2(r,0,maxdep);
    int m;
    scanf("%d",&m);
    int x,k,last=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&k);
        x=(x+last)%n+1,k=(k+last)%n;
        last=query(dep[x],min(maxdep,dep[x]+k),1,maxdep,root[x]);
        printf("%d\n",last);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章