[CF893F]Subtree Minimum Query (主席树)

题面:

传送门:http://codeforces.com/problemset/problem/893/F

题目大意:给你一颗有根树,点有权值,问你每个节点的子树中距离其不超过k的点的权值的最小值。(边权均为1,强制在线


Solution

这题很有意思。

我们一般看到这种距离不超过k的题目,第一反应一般是建以深度为下标,以dfs序为时间轴的的主席树。

很不幸,区间最小值并不能通过减去历史状态得出某个子树的状态

所以说,这题妙在思想的转换。

考虑以dfs序为下标,以深度为时间轴建一颗主席树

我们可以bfs,按深度一层层地把点加进去。

这样子,我们就可以查询对应深度之内的这颗子树的最小权值啦。

就酱,我们就可以把这题切掉啦ヽ( ̄▽ ̄)?


Code

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
long long read()
{
    long long x=0,f=1; char c=getchar();
    while(!isdigit(c)){if(c==‘-‘) f=-1;c=getchar();}
    while(isdigit(c)){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
}
const int N=100000+1000;
const int M=30*N;
int n,r,a[N];
vector <int> e[N];
struct SegmentTree
{
    static const int inf=0x3f3f3f3f;
    #define mid ((now_l+now_r)>>1)
    int MIN[M],son[M][2],cnt;
    inline void update(int now)
    {
        MIN[now]=min(MIN[son[now][0]],MIN[son[now][1]]);
    }
    void Build(int now,int now_l,int now_r)
    {
        if(now_l==now_r)
        {
            MIN[now]=inf;
            return;
        }
        Build(son[now][0]=++cnt,now_l,mid);
        Build(son[now][1]=++cnt,mid+1,now_r);
        update(now);
    }
    void Change(int x,int num,int now,int pre,int now_l,int now_r)
    {
        if(now_l==now_r)
        {
            MIN[now]=num;
            return;
        }
        if(x<=mid) son[now][1]=son[pre][1],Change(x,num,son[now][0]=++cnt,son[pre][0],now_l,mid);
        else son[now][0]=son[pre][0],Change(x,num,son[now][1]=++cnt,son[pre][1],mid+1,now_r);
        update(now);
    }
    int Query(int L,int R,int now,int now_l,int now_r)
    {
        if(now_l>=L and now_r<=R)
            return MIN[now];
        int ans=inf;
        if(L<=mid) ans=min(ans,Query(L,R,son[now][0],now_l,mid));
        if(R>mid) ans=min(ans,Query(L,R,son[now][1],mid+1,now_r));
        return ans;
    }
    void Print(int now,int now_l,int now_r)
    {
        cerr<<"no."<<now<<" now_l&r:"<<now_l<<" "<<now_r<<" sonl&r"<<son[now][0]<<" "<<son[now][1]<<" MIN:"<<MIN[now]<<endl;
        if(now_l!=now_r)
        {
            Print(son[now][0],now_l,mid);
            Print(son[now][1],mid+1,now_r);
        }
    }
    #undef mid
}sgt;
int dfn[N],depth[N],dfn_to,size[N],depth_MAX;
void dfs(int now)
{
    depth_MAX=max(depth_MAX,depth[now]);
    dfn[now]=++dfn_to;
    size[now]=1;
    for(int i=0;i<int(e[now].size());i++)
        if(dfn[e[now][i]]==0)
        {
            depth[e[now][i]]=depth[now]+1;
            dfs(e[now][i]);
            size[now]+=size[e[now][i]];
        }
}
int dl[N],front,tail,root[N];
void bfs()
{
    dl[tail++]=r;
    int depth_now=0;
    while(tail>front)
    {
        int now=dl[front];
        int temp=root[depth_now];
        if(depth[now]!=depth_now)
        {
            depth_now=depth[now];
            temp=root[depth_now-1];
        }
        root[depth_now]=++sgt.cnt;
        sgt.Change(dfn[now],a[now],root[depth_now],temp,1,n);
        //sgt.Print(root[depth_now],1,n);
        //cerr<<endl;
        for(int i=0;i<int(e[now].size());i++)
            if(depth[e[now][i]]>depth[now])
                dl[tail++]=e[now][i];
        front++;
    }
}
int main()
{
    n=read(),r=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    for(int i=1;i<n;i++)
    {
        int s=read(),t=read();
        e[s].push_back(t);
        e[t].push_back(s);
    }

    depth[r]=1;
    dfs(r);
    sgt.Build(0,1,n);
    //sgt.Print(0,1,n);
    //cerr<<endl;
    bfs();

    int m=read(),lans=0;
    for(int i=1;i<=m;i++)
    {
        int x=read(),K=read();
        x=((x+lans)%n)+1,K=(K+lans)%n;
        int temp=min(depth[x]+K,depth_MAX);
        lans=sgt.Query(dfn[x],dfn[x]+size[x]-1,root[temp],1,n);
        printf("%d\n",lans);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/GoldenPotato/p/9814348.html

时间: 2024-11-03 21:53:40

[CF893F]Subtree Minimum Query (主席树)的相关文章

【bzoj1803】Spoj1487 Query on a tree III DFS序+主席树

题目描述 You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels. 输入 The first line contains one integer n (1 <= n <

【BZOJ1803】Spoj1487 Query on a tree III 主席树+DFS序

[BZOJ1803]Spoj1487 Query on a tree III Description You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels. Input

BZOJ_1803_Spoj1487 Query on a tree III_主席树+dfs序

BZOJ_1803_Spoj1487 Query on a tree III_主席树 Description You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels. I

bzoj 1803: Spoj1487 Query on a tree III(主席树)

题意 你被给定一棵带点权的n个点的有根数,点从1到n编号. 定义查询 query(x,k): 寻找以x为根的k大点的编号(从小到大排序第k个点) 假设没有两个相同的点权. 输入格式: 第一行为整数n,第二行为点权,接下来n-1行为树边,接下来一行为整数m,下面m行为两个整数x,k,代表query(x,k) 输出格式: m行,输出每次查询的结果. 题解 先一遍dfs,然后建个主席树,带上去直接跑一跑就好了 我忘了注意dfs序的位置和原来的编号……结果调了半天啥都调不出来…… 1 //minamot

PT07J - Query on a tree III DFS序 + 主席树

dfs序编号后跑权值主席树 但写起来是真的麻烦,总是wa,只能拿出模板过了 #include<bits/stdc++.h> const int N = 100001; using namespace std; struct node { int to, ne; } e[N<<1]; int n, m, len, l1, l2; int id[N], rk[N], eid[N], re[N]; int he[N], a[N], b[N]; int val[N << 5],

LCA+主席树 (求树上路径点权第k大)

SPOJ 10628. Count on a tree (树上第k大,LCA+主席树) 10628. Count on a tree Problem code: COT You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k 

SPOJ 10628 Count on a tree(Tarjan离线LCA+主席树求树上第K小)

COT - Count on a tree #tree You are given a tree with N nodes.The tree nodes are numbered from 1 to N.Each node has an integer weight. We will ask you to perform the following operation: u v k : ask for the kth minimum weight on the path from node u 

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include