传送门
这道题还是比较好的;
我们先考虑树形成一条链的情况,那就成了单点修改 区间查询操作,都是线段树的基本操作;
那么我们就使用dfs序的方法,把它变成一条链,那么某棵子树的区间就是 dfn[i]+tot[i]-1,进行区间修改,单点查询;
通过dfs序,我们实现了从二维降到一维,类似“索引表”的思想,大家可以去看看这道题—海港
#include
#include
#include
#include
using namespace std;
const int maxn=1000001;
const int inf=1e9;
int n,m,tot[maxn],dfn[maxn],idfn[maxn],a[maxn];
struct Node{
int sum,tag;
}tree[maxn<<1|1];
struct Edge{
int next,to;
}edge[maxn<<1];
int num_edge=-1,head[maxn];
void add_edge(int from,int to)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
head[from]=num_edge;
}
int cnt;
void dfs(int x,int fa)
{
dfn[x]=++cnt;
idfn[cnt]=x;
tot[x]=1;
for (int i=head[x]; i!=-1; i=edge[i].next)
{
int to=edge[i].to;
if (to!=fa)
{
dfs(to,x);
tot[x]+=tot[to];
}
}
}
void pushup(int rt) {tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;}
void pushdown(int rt,int ln,int rn)
{
if (tree[rt].tag)
{
tree[rt<<1].tag+=tree[rt].tag;
tree[rt<<1|1].tag+=tree[rt].tag;
tree[rt<<1].sum+=tree[rt].tag*ln;
tree[rt<<1|1].sum+=tree[rt].tag*rn;
tree[rt].tag=0;
}
}
void change(int L,int R,int C,int l,int r,int rt)
{
if (L<=l && r<=R){
tree[rt].sum+=(r-l+1)*C; tree[rt].tag+=C; return;
}
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if (L<=mid) change(L,R,C,l,mid,rt<<1);
if (R>mid) change(L,R,C,mid+1,r,rt<<1|1);
pushup(rt);
}
int ques(int x,int l,int r,int rt)
{
if (l==r && x==l) return tree[rt].sum;
int mid=(l+r)>>1;
pushdown(rt,mid-l+1,r-mid);
if (x<=mid) return ques(x,l,mid,rt<<1);
else return ques(x,mid+1,r,rt<<1|1);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1; i<=n-1; i++)
{
int x,y; scanf("%d%d",&x,&y);
add_edge(x,y); add_edge(y,x);
}
// for (int i=0; i<=num_edge; i++) printf("%d: %d %d\n",i,edge[i^1].to,edge[i].to);
dfs(1,0);
for (int i=1; i<=n; i++)
{
int x; scanf("%d",&x);// printf("%d %d\n",dfn[x],dfn[x]+tot[x]-1);
printf("%d\n",ques(dfn[x],1,n,1));
change(dfn[x],dfn[x]+tot[x]-1,1,1,n,1);
}
// for (int i=1; i<=n; i++) printf("%d %d\n",idfn[i],tot[idfn[i]]);
return 0;
}
我考虑到了如果使用暴力的话相当于把每个点标记一个深度,然后就相当于一个无向图进行搜索,与dfs序有相通之处;
通过这道题终于明白了树链剖分中dfn数组的用处