今天和大家分享两道觉得很不错的线段树的典型题目,是需要树的遍历和线段树结合的题目
-------------------7.27蒟蒻成长日记~~
HDU3974 Assign the task
题目大意是:给你一个类似树的公司领导和下属的关系图,让你动态处理询问 给一个领导和他的全部员工分配同一个工作,询问某一个员工当前的工作是什么。
输入样例:
1
5
4 3
3 2
1 3
5 2
5
C 3
T 2 1
C 3
T 3 2
C 3
输出样例:
Case #1:
-1
1
2
思路:我是第一次做这种树上染色的问题,所以先去看了一篇学长的分享,学习了知识,然后码的代码。抓住重点,这种类似于树上区间染色的问题,最关键的部分就是如何通过dfs来遍历树把树上的节点附上区间。
首先左区间比较好说就是普通的深度优先遍历,遍历到那个点的时候标记一下左区间就好了,关键在于右区间的标记,我们是在回溯的时候把以当前节点为根节点的树的最大的左区间的值作为当前要标记的节点的右区间。这么说还是有点抽象,看下图然后自己脑子模拟一下就好理解多了!
(样例所标记的区间)如图所示:
标记结束区间以后,这个问题就转化成处理区间更新的一个线段树板子题
但是要注意的是分配的任务会有0,因此我们为了区分没有任务和有0这个任务,就在修改的时候把任务的值+1,这样只要在查询的时候再把这个1减掉就可以了~~
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int T,num,k,mx,cnt;
bool st[N];
int n,m;
int ll[N],rr[N];
int runs=1;
char op[2];
int h[N],ne[N],e[N],idx;
struct Node{
int l,r;
int sum;
int lazy;
}tr[N<<2];
void init()
{
memset(h,-1,sizeof h);
memset(st,0,sizeof st);
cnt=0;
idx=0;
}
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int u)
{
ll[u]=++cnt;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
dfs(j);
}
rr[u]=cnt;
}
void pushdown(int u)
{
Node &t=tr[u];
Node &l=tr[u<<1];
Node &r=tr[u<<1|1];
if(t.lazy)
{
l.sum=(l.r-l.l+1)*t.lazy;
l.lazy=t.lazy;
r.sum=(r.r-r.l+1)*t.lazy;
r.lazy=t.lazy;
t.lazy=0;
}
}
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void build(int u,int l,int r)
{
if(l==r)tr[u]={l,r,0,0};
else
{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int c)
{
if(tr[u].l>=l&&tr[u].r<=r)
{
tr[u].sum=(tr[u].r-tr[u].l+1)*c;
tr[u].lazy=c;
}
else
{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)modify(u<<1,l,r,c);
if(r>mid)modify(u<<1|1,l,r,c);
pushup(u);
}
}
int query(int u,int x)
{
if(tr[u].l==x&&tr[u].r==x)return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid)return query(u<<1,x);
else return query(u<<1|1,x);
}
int main()
{
cin>>T;
while(T--)
{
init();
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(b,a);
st[a]=1;
}
for(int i=1;i<=n;i++)
if(!st[i])
{
mx=i;
break;
}
dfs(mx);
build(1,1,cnt);
printf("Case #%d:\n",runs++);
scanf("%d",&m);
while(m--)
{
scanf("%s",op);
if(*op=='C')
{
scanf("%d",&num);
printf("%d\n",query(1,ll[num])-1);
}
else
{
scanf("%d%d",&num,&k);
modify(1,ll[num],rr[num],k+1);
}
}
}
}
2.poj 3321
Apple Tree
题目大意:
给你一个树型结构,要求处理单点修改和树上区间查询操作。
输入样例:
3
1 2
1 3
3
Q 1
C 2
Q 1
输出样例:
3
2
思路:这道题其实是上一道题目的超级简化版本~~去掉了区间修改操作,只需要dfs一下将树上节点进行区间划分就可以了~hh
代码如下:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1e6+10;
bool st[N];
int ll[N],rr[N];
int h[N],e[N],ne[N],idx;
struct Node{
int l,r;
int sum;
}tr[N<<2];
int mx;
int cnt=0;
int n,m;
void dfs(int u)
{
ll[u]=++cnt;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
dfs(j);
}
rr[u]=cnt;
}
void add(int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u].l=l;
tr[u].r=r;
tr[u].sum=1;
}
else
{
tr[u].l=l;
tr[u].r=r;
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int x)
{
if(tr[u].l==x&&tr[u].r==x)
{
tr[u].sum^=1;
}
else
{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid)modify(u<<1,x);
else modify(u<<1|1,x);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r)return tr[u].sum;
else
{
int res=0;
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)res+=query(u<<1,l,r);
if(r>mid)res+=query(u<<1|1,l,r);
return res;
}
}
int main()
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
st[b]=1;
}
for(int i=1;i<=n;i++)
if(!st[i])
{
mx=i;
break;
}
dfs(mx);
build(1,1,cnt);
cin>>m;
char op[2];
int num;
while(m--)
{
scanf("%s%d",op,&num);
if(*op=='Q')printf("%d\n",query(1,ll[num],rr[num]));
else modify(1,ll[num]);
}
}
参考:HDU - 3974 Assign the task (dfs序+线段树)_AC__dream的博客-CSDN博客
特别感谢聪神的这篇博客对我学习这个dfs序+线段树的帮助~~~聪神yyds