[BZOJ1036][ZJOI2008]树的统计Count(树链剖分)

来源:互联网 发布:java代码实现单例模式 编辑:程序博客网 时间:2024/04/30 03:54

题目:

我是超链接

解析:

树链剖分模板题

建树过程O(nlogn)
每次查询经过O(logn)条重链,对于每条重链在线段树上查询复杂度为O(logn);总复杂度为O(mlog^2n)
树链剖分的话:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html
#include <cstdio>#include <cstring>#include <iostream>#define N 30000#define MIN -2100000000using namespace std;//w[i]表示点i的权值,size[i]表示以i为根的子树的结点个数,deep[i]表示点i的深度,fa[i]表示点i的父亲,son[i]表示点i的重儿子 int size[N*4],w[N*4],son[N*4],fa[N*4],deep[N*2+5];//top[i]表示点i所在的链的最头上的点,num[i]表示点i在线段树数组中对应的编号,tree[i]表示在线段树数组中编号为i的点在树中对应的点的编号 int top[N*4],num[N*4],tree[N*4],totw=0;int sum[N*4],maxn[N*4];int next[N*2+5],point[N*2+5],v[N*2+5],tot=0;void addline(int x,int y){++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;}void dfs_1(int now,int dep,int faa){deep[now]=dep;size[now]=1;fa[now]=faa;int maxx=0,i;for (i=point[now];i;i=next[i])  if (v[i]!=faa)   {dfs_1(v[i],dep+1,now);size[now]+=size[v[i]];if (size[v[i]]>maxx){maxx=size[v[i]];son[now]=v[i];}   }}void dfs_2(int now,int faa) {if (son[faa]!=now) top[now]=now;else top[now]=top[fa[now]];num[now]=++totw;if (son[now]) //是叶子节点{dfs_2(son[now],now);for (int i=point[now];i;i=next[i])  if (v[i]!=faa&&v[i]!=son[now])  dfs_2(v[i],now);}}void updatasum(int now){sum[now]=sum[now<<1]+sum[(now<<1)+1];}void updatamaxn(int now){maxn[now]=max(maxn[now<<1],maxn[(now<<1)+1]);}void buildsum(int now,int l,int r){if (l==r){sum[now]=w[tree[l]];return;}int mid=(l+r)>>1;buildsum(now<<1,l,mid);buildsum((now<<1)+1,mid+1,r);updatasum(now);}void buildmaxn(int now,int l,int r){if (l==r){maxn[now]=w[tree[l]];return;}int mid=(l+r)>>1;buildmaxn(now<<1,l,mid);buildmaxn((now<<1)+1,mid+1,r);updatamaxn(now);}void changesum(int now,int l,int r,int x,int v){if (l==r){sum[now]=v;return;}int mid=(l+r)>>1;if (x<=mid)  changesum(now<<1,l,mid,x,v);else  changesum((now<<1)+1,mid+1,r,x,v);updatasum(now);}void changemaxn(int now,int l,int r,int x,int v){if (l==r){maxn[now]=v;return;}int mid=(l+r)>>1;if (x<=mid)  changemaxn(now<<1,l,mid,x,v);else  changemaxn((now<<1)+1,mid+1,r,x,v);updatamaxn(now);}int qmax(int now,int l,int r,int lrange,int rrange){    if (l>=lrange&&r<=rrange)      return maxn[now];    int mid=(l+r)>>1;int ans=MIN;    if (mid>=lrange)  ans=max(ans,qmax(now<<1,l,mid,lrange,rrange));if (mid<rrange)  ans=max(ans,qmax((now<<1)+1,mid+1,r,lrange,rrange));return ans; }int qsum(int now,int l,int r,int lrange,int rrange){    if (l>=lrange&&r<=rrange)      return sum[now];    int mid=(l+r)>>1,ans=0;    if (mid>=lrange)  ans+=qsum(now<<1,l,mid,lrange,rrange);if (mid<rrange)  ans+=qsum((now<<1)+1,mid+1,r,lrange,rrange);return ans; }int main(){int i,n,a,b,q,u,v;scanf("%d",&n);for (i=1;i<=n-1;i++){scanf("%d%d",&a,&b);addline(a,b);}      for (i=1;i<=n;i++)      scanf("%d",&w[i]);                                                                                                        dfs_1(1,1,0);    dfs_2(1,0);        for (i=1;i<=n;i++)      tree[num[i]]=i;      buildsum(1,1,n);    buildmaxn(1,1,n);    scanf("%d",&q);    for (i=1;i<=q;i++)    {    char st[10];    scanf("%s%d%d",&st,&u,&v);    if (st[0]=='C')    {    changemaxn(1,1,n,num[u],v);//u点的权值改为v     changesum(1,1,n,num[u],v);}else  if (st[1]=='M')  {  int f1=top[u],f2=top[v];            int maxx=MIN;while (f1!=f2)            {            if (deep[f1]<deep[f2]) swap(u,v);                f1=top[u],f2=top[v];//f1比较深             maxx=max(maxx,qmax(1,1,n,num[f1],num[u]));            u=fa[f1];            f1=top[u];}if (num[u]<num[v]) swap(u,v);maxx=max(maxx,qmax(1,1,n,num[v],num[u]));  printf("%d\n",maxx);//点l到点r的最大值   }else{int f1=top[u],f2=top[v];            int maxx=0;while (f1!=f2)            {            if (deep[f1]<deep[f2]) swap(u,v);                f1=top[u],f2=top[v];//f1比较深             maxx+=qsum(1,1,n,num[f1],num[u]);            u=fa[f1];            f1=top[u];}if (num[u]<num[v]) swap(u,v);maxx+=qsum(1,1,n,num[v],num[u]);  printf("%d\n",maxx);//点l到点r的最大值 }  }}
注意:

求最大值的时候最小值要设为负数...........不可以为0...........

0 0
原创粉丝点击