n个点的带点权带边权的树,设点权为a[i],边权为b[i]

一棵树有n*(n-1)/2个点对,

定义这棵树的价值为任意两点对的(a[x]^a[y])*dis(x,y)

有m次修改一个点的点权的操作

输出每次修改完点权后这颗树的价值

第i次的修改会影响到第i次之后的修改

第一眼:我靠怎么又是点分树

然后因为没有处理好选的点对和自己处于根的同一子树,一下午过去了,一晚上过去了,又一个晚上过去了。。。。。。

吐槽结束,下面是题解

异或-->按位操作,点权变为1或者0,第i位的答案乘上2^i即可

点分治是每次查询跨越分治重心的点的贡献

为了支持在修改之后快速查询

我们依次查询每个点的贡献

将答案除以2就是对于初始的树的答案

对于每次的修改操作

查询出这个点的原贡献,在答案中减去

然后修改点权

再查询这个点新的贡献,再答案中加上

这样单次操作就可以log时间解决

查询的具体细节决定了点分树上要维护哪些信息

假设之前已经枚举了是第几位

假设现在查询点x对答案的贡献,x在点分树上第i层的祖先为F[x][i]

设cnt[i][0/1] 表示点分树上点i的子树内,这一位是0/1的点的个数

sum[i][0/1]表示点分树上点i的子树内,这一位是0/1的点到i的距离和

若x的这一位为t

那么x对答案的贡献分为下图中粉色和绿色的两部分

粉色部分=长度*次数==  dis(x,F[x][i])*(cnt[F[x][i]][t^1] - 与x属于R的同一子树 且 这一位为t^1 的点的数量)

绿色部分=sum[F[x][i]][t^1] - 与x属于R的同一子树 且 这一位为t^1 的点到R的距离和

与x属于R的同一子树 且 这一位为t^1 的点的数量就等于cnt [ F[x][i+1] ][t^1]

设x在“原树”上R的子节点中y的子树内

与x属于R的同一子树 且 这一位为t^1 的点到R的距离和=原树上y的子树对R的贡献

所以还需要维护每一层分治重心在原树中的直接子节点对分治重心的贡献

如何记录这个?

一个点可能是多个分治重心在原树中的直接子节点

令bl[i][j]=k 表示点i属于其第j层祖先的子树kk,这个kk映射之后是k

fsum[k][0/1] 映射之后的点k对其在原树上的父节点在点分树中 的贡献

所以 与x属于R的同一子树 且 这一位为t^1 的点到R的距离和 = fsum[bl[x][i]][t^1]

学会了代码完全bfs,不用担心windows爆栈了,O(∩_∩)O哈哈~

#include<cstdio>
#include<iostream> using namespace std; typedef long long LL; #define N 30001 int a[N];
int front[N],to[N<<],nxt[N<<],val[N<<],tot; int F[N][];//F[i][j]=k 点分树上点i第j层的祖先是k
int D[N][];//D[i][j]=k 点分树上点i到其第j层祖先的距离为k
int dep[N];//dep[i]=j 点i处于点分树上第j层
int cnt[N][][];//cnt[i][j][0/1] 点分树上点i子树内点权第j位为0/1的点的数量
LL sum[N][][];//sum[i][j][0/1] 点分树上点i子树内点权第j位为0/1的点到点i的距离和
int bl[N][];//bl[i][j]=k 点i属于其第j层祖先的子树kk,kk映射之后是k
LL fsum[N][][];//fsum[i][j][0/1] 映射之后的点i对其在原树上的父节点在点分树中 的贡献
int id;//映射编号 int q[N];
int fa[N],siz[N],mx[N]; bool vis[N]; int dis[N]; void read(int &x)
{
x=; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
} void add(int u,int v,int w)
{
to[++tot]=v; nxt[tot]=front[u]; front[u]=tot; val[tot]=w;
to[++tot]=u; nxt[tot]=front[v]; front[v]=tot; val[tot]=w;
} int getroot(int x)
{
int l=,r=,now;
q[r++]=x;
while(l<r)
{
now=q[l++];
siz[now]=;
mx[now]=;
for(int i=front[now];i;i=nxt[i])
if(!vis[to[i]] && to[i]!=fa[now])
{
fa[to[i]]=now;
q[r++]=to[i];
}
}
for(int i=r-;i;--i)
{
siz[fa[q[i]]]+=siz[q[i]];
mx[fa[q[i]]]=max(mx[fa[q[i]]],siz[q[i]]);
}
for(int i=;i<r;++i) mx[q[i]]=max(mx[q[i]],siz[q[]]-siz[q[i]]);
int big=q[];
for(int i=;i<r;++i)
if(mx[q[i]]<mx[big]) big=q[i];
return big;
} void add1(int rt,int x)
{
for(int i=;i<;++i)
{
cnt[rt][i][a[x]>>i&]++;
sum[rt][i][a[x]>>i&]+=dis[x];
}
} void add2(int rt,int x)
{
for(int i=;i<;++i) fsum[rt][i][a[x]>>i&]+=dis[x];
} void bfs(int x,int d)//x为目前点分树上根节点
{
int l=,r=,now;
fa[x]=; dis[x]=;
q[r++]=x;
while(l<r)
{
now=q[l++];
F[now][++dep[now]]=x;
D[now][dep[now]]=dis[now];
add1(x,now);
for(int i=front[now];i;i=nxt[i])
if(!vis[to[i]] && to[i]!=fa[now])
{
dis[to[i]]=dis[now]+val[i];
fa[to[i]]=now;
q[r++]=to[i];
}
}
for(int i=front[x];i;i=nxt[i])
{
if(vis[to[i]]) continue;
l=r=;
q[r++]=to[i];
fa[q[]]=;
id++;
while(l<r)
{
now=q[l++];
bl[now][d]=id;
add2(id,now);
for(int i=front[now];i;i=nxt[i])
if(!vis[to[i]] && to[i]!=fa[now])
{
fa[to[i]]=now;
q[r++]=to[i];
}
}
}
} void build(int x,int d)
{
int big=getroot(x);
// printf("第%d层分治重心是%d\n",d,big);
vis[big]=true;
bfs(big,d);
for(int i=front[big];i;i=nxt[i])
if(!vis[to[i]]) build(to[i],d+);
} LL query(int x)
{
LL ans=;
int t;
// int aa,bb,cc;
for(int i=;i<dep[x];++i)
for(int j=;j<;++j)
{
t=a[x]>>j&;
ans+=1LL*(cnt[F[x][i]][j][t^]-cnt[F[x][i+]][j][t^])*D[x][i]+(sum[F[x][i]][j][t^]-fsum[bl[x][i]][j][t^])<<j;
// aa=(cnt[F[x][i]][j][t^1]-cnt[F[x][i+1]][j][t^1])*D[x][i];
// bb=sum[F[x][i]][j][t^1];
// cc=fsum[bl[x][i]][j][t^1];
}
for(int j=;j<;++j)
{
t=a[x]>>j&;
ans+=1LL*sum[x][j][t^]<<j;
}
return ans;
} void change(int x,int y)
{
int t;
for(int j=;j<;++j)
{
if((a[x]>>j&)==(y>>j&)) continue;
t=a[x]>>j&;
for(int i=;i<=dep[x];++i)
{
cnt[F[x][i]][j][t]--;
sum[F[x][i]][j][t]-=D[x][i];
cnt[F[x][i]][j][t^]++;
sum[F[x][i]][j][t^]+=D[x][i];
if(i<dep[x])
{
fsum[bl[x][i]][j][t]-=D[x][i];
fsum[bl[x][i]][j][t^]+=D[x][i];
}
}
}
a[x]=y;
} void out(LL x)
{
if(x>) out(x/);
putchar(x%+'');
} int main()
{
//freopen("data.in","r",stdin);
//freopen("tree.out","w",stdout);
int n,m;
read(n);
for(int i=;i<=n;++i) read(a[i]);
int u,v,w;
for(int i=;i<n;++i)
{
read(u); read(v); read(w);
add(u,v,w);
}
build(,);
LL ans=;
for(int i=;i<=n;++i)
{
ans+=query(i);
// cout<<i<<' '<<query(i)<<'\n';
}
ans>>=;
// cout<<ans<<'\n';
/* for(int i=1;i<=n;++i)
{
for(int j=1;j<dep[i];++j) printf("%d ",bl[i][j]);
printf("\n");
}*/
read(m);
while(m--)
{
read(u); read(v);
ans-=query(u);
change(u,v);
ans+=query(u);
out(ans);
putchar('\n');
}
// printf("%d",id);
}

cdqz2017-test8-Tree(点分树)的更多相关文章

  1. 1043 Is It a Binary Search Tree (25分)(树的插入)

    A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...

  2. 【点分树】codechef Yet Another Tree Problem

    已经连咕了好几天博客了:比较经典的题目 题目大意 给出一个 N 个点的树和$K_i$, 求每个点到其他所有点距离中第 $K_i$ 小的数值. 题目分析 做法一:点分树上$\log^3$ 首先暴力做法: ...

  3. 一篇自己都看不懂的点分治&点分树学习笔记

    淀粉质点分治可真是个好东西 Part A.点分治 众所周知,树上分治算法有$3$种:点分治.边分治.链分治(最后一个似乎就是树链剖分),它们名字的不同是由于分治方式的不同的.点分治,顾名思义,每一次选 ...

  4. BZOJ4317Atm的树&BZOJ2051A Problem For Fun&BZOJ2117[2010国家集训队]Crash的旅游计划——二分答案+动态点分治(点分树套线段树/点分树+vector)

    题目描述 Atm有一段时间在虐qtree的题目,于是,他满脑子都是tree,tree,tree…… 于是,一天晚上他梦到自己被关在了一个有根树中,每条路径都有边权,一个神秘的声音告诉他,每个点到其他的 ...

  5. 【学习笔记 边分树】【uoj400】【CTSC2018】暴力写挂

    题目 描述 ​ 有两棵树\(T\)和\(T'\),节点个数都为\(n\),根节点都为\(1\)号节点; ​ 求两两点之间 $$ \begin{align} depth(x) + depth(y) - ...

  6. 2018.08.28 洛谷P3345 [ZJOI2015]幻想乡战略游戏(点分树)

    传送门 题目就是要求维护带权重心. 因此破题的关键点自然就是带权重心的性质. 这时发现直接找带权重心是O(n)的,考虑优化方案. 发现点分树的树高是logn级别的,并且对于以u为根的树,带权重心要么就 ...

  7. PTA 04-树5 Root of AVL Tree (25分)

    题目地址 https://pta.patest.cn/pta/test/16/exam/4/question/668 5-6 Root of AVL Tree   (25分) An AVL tree ...

  8. PAT 甲级 1064 Complete Binary Search Tree (30 分)(不会做,重点复习,模拟中序遍历)

    1064 Complete Binary Search Tree (30 分)   A Binary Search Tree (BST) is recursively defined as a bin ...

  9. PAT 甲级 1043 Is It a Binary Search Tree (25 分)(链表建树前序后序遍历)*不会用链表建树 *看不懂题

    1043 Is It a Binary Search Tree (25 分)   A Binary Search Tree (BST) is recursively defined as a bina ...

随机推荐

  1. PowerShell一例

    (Get-WmiObject -query ‘select * from SoftwareLicensingService’).OA3xOriginalProductKey

  2. PGSQL 获取数据库大小以及表达小等的SQL

    SELECT d.datname AS Name, pg_catalog.pg_get_userbyid(d.datdba) AS Owner, CASE WHEN pg_catalog.has_da ...

  3. CIO知识储备

    1.IT安全和法规知识是CIO的首要 2.IT项目管理专业知识是CIO的必备 3.合作伙伴管理和供应商管理对成功也很关键 4.企业数据管理技能对CIO越来越重要 5.企业财务技能是CIO的一种必备 6 ...

  4. Angular生成二维码

    Installation - Angular 5+, Ionic NPM npm install angularx-qrcode --save Yarn yarn add angularx-qrcod ...

  5. Async和Await 异步方法

    Async和Await关键字是C#异步编程的核心.通过使用这两个关键字,你可以使用.NET Framework或Windows Runtime的资源创建一个异步方法如同你创建一个同步的方法一样容易.通 ...

  6. Get The Treasury HDU - 3642(扫描线求三维面积交。。体积交)

    题意: ...就是求体积交... 解析: 把每一层z抽出来,计算面积交, 然后加起来即可..! 去看一下 二维面积交的代码 再看看这个三维面积交的代码.. down函数里 你发现了什么规律!!! 参考 ...

  7. 【POJ 2251】Dungeon Master(bfs)

    BUPT2017 wintertraining(16) #5 B POJ - 2251 题意 3维的地图,求从S到E的最短路径长度 题解 bfs 代码 #include <cstdio> ...

  8. Android8.0运行时权限策略变化和适配方案

    版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.comAndroid8.0也就是Android O即将要发布了,有很多新特性,目前我们可以通过AndroidS ...

  9. 洛谷 画栅栏Painting the Fence 解题报告

    P2205 画栅栏Painting the Fence 题目描述 \(Farmer\) \(John\) 想出了一个给牛棚旁的长围墙涂色的好方法.(为了简单起见,我们把围墙看做一维的数轴,每一个单位长 ...

  10. luogu1712 区间 (尺取法+线段树)

    先把区间按照长度从小到大排序,然后用尺取法来做 大概就是先一点一点把区间算上 直到某个点被覆盖了m次,然后一点一点把最前面的区间扔掉,直到没有点被覆盖m次,这样反复做(相当于是它选择的区间左右端点在那 ...