题意:有一颗树,树上每个点有给定点权,有m次询问,每次询问点x的所有子树中,与x的距离小于等于k的所有点的点权最小值是多少。题目要求强制在线。
思路:
主席树解法:按照dfs序在树上建立主席树,询问是查询x节点的管辖的那段区间,属于经典操作,问题在于如何控制距离小于等于k,于是我们可以按照点的深度来建主席树,虽然最小值问题不满足前缀相减的性质,但实际上我们并不需要减掉1到dep[x]这段区间,因为x节点dfs序管辖范围的限制,前面的点不会对答案产生影响,所以直接查询1到dep[x]+k这段区间即可。
线段树合并解法:以点的深度为基准来建线段树,对每个点都维护一颗线段树,再进行线段树合并即可。要注意的一点是由于询问的是最小值,而数组的初始值默认为0,所以在线段树合并时要开新点来合并,而不能直接利用已有的点,并且新点对应的最小值为要合并的两个点的最小值,而不能直接取左右儿子的最小值。
以前一直觉得线段树合并和主席树非常类似,但是又说不出个所以然,这道题很好的展现了线段树合并与主席树的异同点。
主席树代码:
#include
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[maxn];
vector 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 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
线段树合并代码:
#include
using namespace std;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[maxn];
vector 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;imid)
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