您的位置:首页 > 其它

BZOJ 1036: [ZJOI2008]树的统计Count

2014-06-13 15:52 417 查看

Description

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

Input

输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。 对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。

Output

对于每个“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

题解

树链剖分模板。3000多ms。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
using namespace std;
int n,zz,head[30002],v[30002];
struct bian {int to,nx;} e[60005];
int h[30002],fa[30002][16],son[30002];
bool vis[30002];
int size,bl[30002],tw[30002];
//belong数组,记录每个点所在链的编号,tw数组表示点i在线段树上的位置*
struct shu {int l,r,sum,mx;} tr[120002];
void insert(int x,int y)
{
zz++; e[zz].to=y; e[zz].nx=head[x]; head[x]=zz;
zz++; e[zz].to=x; e[zz].nx=head[y]; head[y]=zz;
}
void init()//输入1,处理树上的边
{
scanf("%d",&n);
int x,y;
for(int i=1;i<n;i++)
{scanf("%d%d",&x,&y); insert(x,y);}
for(int i=1;i<=n;i++) scanf("%d",&v[i]);
}
void dfs1(int x)//处理深度h,和LCA的dfs相同,顺便再处理出每个子树的结点个数。
{
vis[x]=1; son[x]=1;
for(int i=1;i<=14;i++)
{if(h[x]<(1<<i)) break;
fa[x][i]=fa[fa[x][i-1]][i-1];
}
for(int i=head[x];i;i=e[i].nx)
{if(vis[e[i].to]) continue;
h[e[i].to]=h[x]+1; fa[e[i].to][0]=x;
dfs1(e[i].to);
son[x]+=son[e[i].to];
}
}
void dfs2(int x,int ll/*表示所在链的编号*/)//处理链
{
int k=0; size++;
tw[x]=size; bl[x]=ll;
for(int i=head[x];i;i=e[i].nx)
{if(h[e[i].to]>h[x]&&son[e[i].to]>son[k]) k=e[i].to;}
if(k==0) return;
dfs2(k,ll);
for(int i=head[x];i;i=e[i].nx)
{if(h[e[i].to]>h[x]&&e[i].to!=k) dfs2(e[i].to,e[i].to);}
}
void build(int w,int l,int r)//线段树建树
{
tr[w].l=l; tr[w].r=r;
if(l==r) return;
int mid=(l+r)>>1;
build(w<<1,l,mid); build((w<<1)+1,mid+1,r);
}
void change(int w,int x,int y)//权值修改(单点)
{
if(tr[w].l==tr[w].r) {tr[w].mx=tr[w].sum=y; return;}
int mid=(tr[w].l+tr[w].r)>>1;
if(x<=mid) change(w<<1,x,y);
else change((w<<1)+1,x,y);
tr[w].sum=tr[w<<1].sum+tr[(w<<1)+1].sum;
tr[w].mx=max(tr[w<<1].mx,tr[(w<<1)+1].mx);
}
int lca(int x,int y)
{
if(h[x]<h[y]) swap(x,y);
int t=h[x]-h[y];
for(int i=0;i<=14;i++)
{if(t&(1<<i)) x=fa[x][i];}
for(int i=14;i>=0;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][0];
}
int getm(int w,int l,int r)
{
int x=tr[w].l,y=tr[w].r;
if(l==x&&r==y) return tr[w].mx;
int mid=(x+y)>>1;
if(r<=mid) return getm(w<<1,l,r);
else if(l>mid) return getm((w<<1)+1,l,r);
else return max(getm(w<<1,l,mid),getm((w<<1)+1,mid+1,r));
}
int fmax(int x,int y)
{
int big=-0x7fffffff;
while(bl[x]!=bl[y])
{big=max(big,getm(1,tw[bl[x]],tw[x]));
x=fa[bl[x]][0];
}
big=max(big,getm(1,tw[y],tw[x]));
return big;
}
int gets(int w,int l,int r)
{
int x=tr[w].l,y=tr[w].r;
if(l==x&&y==r)return tr[w].sum;
int mid=(x+y)>>1;
if(r<=mid)return gets(w<<1,l,r);
else if(l>mid)return gets((w<<1)+1,l,r);
else return gets(w<<1,l,mid)+gets((w<<1)+1,mid+1,r);
}
int fsum(int x,int y)
{
int sum=0;
while(bl[x]!=bl[y])
{sum+=gets(1,tw[bl[x]],tw[x]);
x=fa[bl[x]][0];
}
sum+=gets(1,tw[y],tw[x]);
return sum;
}
void work()
{
build(1,1,n);
for(int i=1;i<=n;i++) change(1,tw[i],v[i]);
int k,x,y; char ch[7];
scanf("%d",&k);
for(int i=1;i<=k;i++)
{scanf("%s%d%d",ch,&x,&y);
if(ch[0]=='C') {v[x]=y; change(1,tw[x],y);}
else
{int t=lca(x,y); //cout<<t<<" ";
if(ch[1]=='M') printf("%d\n",max(fmax(x,t),fmax(y,t)));
else printf("%d\n",fsum(x,t)+fsum(y,t)-v[t]);
}
}
}
int main()
{
init(); dfs1(1); dfs2(1,1); work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树链剖分