C++之路进阶——codevs2460(树的统计)

时间:2022-11-22 20:12:03

2460 树的统计

2008年省队选拔赛浙江

 时间限制: 2 s
 空间限制: 128000 KB
 题目等级 : 大师 Master
 
 
 
题目描述 Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。

我们将以下面的形式来要求你对这棵树完成一些操作:

  1. I.                    CHANGE u t : 把结点u的权值改为t
  2. II.                 QMAX u v: 询问从点u到点v的路径上的节点的最大权值
  3. III.               QSUM u v: 询问从点u到点v的路径上的节点的权值和

 

注意:从点u到点v的路径上的节点包括u和v本身

输入描述 Input Description

输入文件的第一行为一个整数n,表示节点的个数。

接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。

       接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。

接下来1行,为一个整数q,表示操作的总数。

接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。

输出描述 Output Description

对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。

样例输入 Sample Input

4

1 2

2 3

4 1

4 2 1 3

12

QMAX 3 4

QMAX 3 3

QMAX 3 2

QMAX 2 3

QSUM 3 4

QSUM 2 1

CHANGE 1 5

QMAX 3 4

CHANGE 3 6

QMAX 3 4

QMAX 2 4

QSUM 3 4

样例输出 Sample Output

4

1

2

2

10

6

5

6

5

16

数据范围及提示 Data Size & Hint

对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

题解

裸的树链剖分...

树链剖分就是把树拆成一系列链,然后用数据结构对链进行维护。

通常的剖分方法是轻重链剖分,所谓轻重链就是对于节点u的所有子结点v,size[v]最大的v与u的边是重边,其它边是轻边,其中size[v]是以v为根的子树的节点个数,全部由重边组成的路径是重路径,根据论文上的证明,任意一点到根的路径上存在不超过logn条轻边和logn条重路径。

这样我们考虑用数据结构来维护重路径上的查询,轻边直接查询。

通常用来维护的数据结构是线段树,splay较少见。

具体步骤

预处理

第一遍dfs求出树每个结点的深度deep[x],其为根的子树大小size[x]

以及祖先的信息fa[x][i]表示x往上距离为2^i的祖先

第二遍dfs

ž根节点为起点,向下拓展构建重链

选择最大的一个子树的根继承当前重链

其余节点,都以该节点为起点向下重新拉一条重链

ž给每个结点分配一个位置编号,每条重链就相当于一段区间,用数据结构去维护。

把所有的重链首尾相接,放到同一个数据结构上,然后维护这一个整体即可

修改操作

ž1、单独修改一个点的权值

根据其编号直接在数据结构中修改就行了。

2、修改点u和点v的路径上的权值

(1)若u和v在同一条重链上

直接用数据结构修改pos[u]至pos[v]间的值。

(2)若u和v不在同一条重链上

一边进行修改,一边将u和v往同一条重链上靠,然后就变成了情况(1)。

查询操作

ž查询操作的分析过程同修改操作

题目不同,选用不同的数据结构来维护值,通常有线段树和splay

代码:

树链剖分

 #include<cstdio>
#include<iostream>
#include<cstring>
#define INF 0x7fffffff
#define N 30005
#define M 60005 using namespace std; int n,q,cnt,sz;
int fa[N][],v[N],deep[N],size[N],head[N];
int pos[N],belong[N];
bool vis[N];
struct node
{
int to;
int next;
}e[M]; struct ss
{
int l;
int r;
int mx;
int sum;
}t[]; void insert(int u,int v)
{
e[++cnt].to=v; e[cnt].next=head[u]; head[u]=cnt;
e[++cnt].to=u; e[cnt].next=head[v]; head[v]=cnt;
} void inint()
{
scanf("%d",&n);
for (int i=;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
insert(x,y);
}
for (int i=;i<=n;i++) scanf("%d",&v[i]);
} void getree(int x)
{
size[x]=;
vis[x]=;
for (int i=;i<=;i++)
{
if (deep[x]<(<<i)) break;
fa[x][i]=fa[fa[x][i-]][i-];
}
for (int i=head[x];i;i=e[i].next)
{
if (vis[e[i].to]) continue;
deep[e[i].to]=deep[x]+;
fa[e[i].to][]=x;
getree(e[i].to);
size[x]+=size[e[i].to];
}
} void dfs(int x,int chain)
{
int k=;
sz++;
pos[x]=sz;
belong[x]=chain;
for (int i=head[x];i;i=e[i].next)
if (deep[e[i].to]>deep[x]&&size[e[i].to]>size[k])
k=e[i].to;
if (k==) return;
dfs(k,chain);
for (int i=head[x];i;i=e[i].next)
if (deep[e[i].to]>deep[x]&&k!=e[i].to)
dfs(e[i].to,e[i].to);
} int lca(int x,int y)
{
if (deep[x]<deep[y]) swap(x,y);
int t=deep[x]-deep[y];
for (int i=;i<=;i++)
if ((<<i)&t) x=fa[x][i];
for (int i=;i>=;i--)
if (fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
if (x==y) return x;
else return fa[x][];
} void build(int k,int l,int r)
{
t[k].l=l;
t[k].r=r;
if (l==r) return;
int mid=(l+r)>>;
build(k<<,l,mid);
build(k<<|,mid+,r);
} void change(int k,int x,int y)
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>;
if (l==r)
{
t[k].sum=t[k].mx=y;
return;
}
if (x<=mid) change(k<<,x,y);
else change(k<<|,x,y);
t[k].sum=t[k<<].sum+t[k<<|].sum;
t[k].mx=max(t[k<<].mx,t[k<<|].mx);
} int find_sum(int k,int x,int y)
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>;
if (l==x&&y==r) return t[k].sum;
if (y<=mid) return find_sum(k<<,x,y);
else if (x>mid) return find_sum(k<<|,x,y);
else return find_sum(k<<,x,mid)+find_sum(k<<|,mid+,y);
} int find_mx(int k,int x,int y)
{
int l=t[k].l,r=t[k].r,mid=(l+r)>>;
if (l==x&&r==y) return t[k].mx;
if (y<=mid) return find_mx(k<<,x,y);
else if (x>mid) return find_mx(k<<|,x,y);
else return max(find_mx(k<<,x,mid),find_mx(k<<|,mid+,y));
} int solve_mx(int x,int f)
{
int mx=-INF;
while(belong[x]!=belong[f])
{
mx=max(mx,find_mx(,pos[belong[x]],pos[x]));
x=fa[belong[x]][];
}
mx=max(mx,find_mx(,pos[f],pos[x]));
return mx;
} int solve_sum(int x,int f)
{
int sum=;
while (belong[x]!=belong[f])
{
sum+=find_sum(,pos[belong[x]],pos[x]);
x=fa[belong[x]][];
}
sum+=find_sum(,pos[f],pos[x]);
return sum;
} void solve()
{
build(,,n);
for (int i=;i<=n;i++)
change(,pos[i],v[i]);
scanf("%d",&q);
char ch[];
for (int i=;i<=q;i++)
{
int x,y;
scanf("%s%d%d",ch,&x,&y);
if (ch[]=='C')
{
v[x]=y;
change(,pos[x],y);
}
else
{
int t=lca(x,y);
if (ch[]=='M')
printf("%d\n",max(solve_mx(x,t),solve_mx(y,t)));
else
printf("%d\n",solve_sum(x,t)+solve_sum(y,t)-v[t]);
}
}
} int main()
{
inint();
getree();
dfs(,);
solve();
return ;
}

块状树:

 #include<bits/stdc++.h>
#define maxn (int)1e5+10 using namespace std; vector<int>g[maxn],ge[maxn];
int cnt,sqrtn,n,m;
int w[maxn],sum[maxn],mx[maxn],fa[maxn],deep[maxn],bel[maxn],size[maxn]; void dfs(int u)
{
for(int i=;i<g[u].size();i++)
{
int v=g[u][i];
if(v==fa[u])continue;
fa[v]=u;
deep[v]=deep[u]+;
if(size[bel[u]]<sqrtn)
{
size[bel[u]]++;bel[v]=bel[u];
ge[u].push_back(v);
}
dfs(v);
}
} void dfs(int u,int s,int maxx)
{
s+=w[u];
sum[u]=s;
maxx=max(maxx,w[u]);
mx[u]=maxx;
for(int i=;i<ge[u].size();i++)
dfs(ge[u][i],s,maxx);
} void change(int u,int ww)
{
w[u]=ww;
if(bel[u]==u) dfs(u,,INT_MIN);
else dfs(u,sum[fa[u]],mx[fa[u]]);
} pair<int,int> qsumax(int u,int v)
{
int s=,maxx=INT_MIN;
while(u!=v){
if(deep[u]<deep[v])swap(u,v);
if(bel[u]==bel[v])
{
s+=w[u];maxx=max(maxx,w[u]);
u=fa[u];
}
else
{
if(deep[bel[u]]<deep[bel[v]])swap(u,v);
s+=sum[u],maxx=max(maxx,mx[u]);
u=fa[bel[u]];
}
}
s+=w[u],maxx=max(maxx,w[u]);
return pair<int,int>(s,maxx);
} int main()
{
scanf("%d",&n);
sqrtn=sqrt(n);
for (int i=;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
for (int i=;i<=n;i++)
scanf("%d",w+i),bel[i]=i,size[i]=;
dfs();
int q;
for(int i=;i<=n;i++)if(bel[i]==i)
dfs(i,,INT_MIN);
scanf("%d",&q);
while (q--)
{
int x,y;
char op[];
scanf("%s%d%d",op,&x,&y);
if(op[]=='C') change(x,y);
else
{
pair<int,int>anss=qsumax(x,y);
if(op[]=='M')
printf("%d\n",anss.second);
else printf("%d\n",anss.first);
}
}
return ;
}