Description

有一棵树,要求支持

  1. 查询两点间简单路径的所有子链的异或和的和
  2. 修改某条边的权值

Solution

这种树上异或问题首先应该想到对于每个点存下一个前缀异或和表示这个点到根节点路径的异或和。那么两点之间路径的异或和就等于这两点的前缀和再异或起来。

于是操作一变成了:有k个点,每个点有权值,问\(\sum \limits_{i=1}^k\sum\limits_{j=i+1}^k val[i]\oplus val[j]\)

由于是异或运算,我们按位考虑。

对于二进制位 \(p\),假设这 \(k\) 个数中有 \(x\) 个的第 \(p\) 位为1,剩下的为 \(0\),那么对答案有贡献的实际上就只有 \(x\times (k-x)\) 个点对,也就是说只有这么多点对异或起来的值为 \(1\)。这启示我们对于每个二进制位,都找到多少位是0,多少位是1,把他们乘起来就好了。

Code

#include<cstdio>
#include<cctype>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 30005
using std::min;
using std::max;
using std::swap;
#define int long long
#define ls cur<<1,l,mid,ql,qr
#define rs cur<<1|1,mid+1,r,ql,qr int head[N],dfn[N],top[N],d[N];
int n,m,cnt,tot,lazy[N<<2],cme[N];
int sze[N],son[N],dis[N],fs[N],fa[N]; struct Edge{
int to,nxt,dis;
}edge[N<<1]; struct Node{
int a[12][2]; friend Node operator+(Node x,Node y){
Node z;memset(z.a,0,sizeof z.a);
for(int i=1;i<=10;i++){
z.a[i][0]=x.a[i][0]+y.a[i][0];
z.a[i][1]=x.a[i][1]+y.a[i][1];
} return z;
}
}sum[N<<2]; void add(int x,int y,int z){
edge[++cnt].to=y;
edge[cnt].nxt=head[x];
edge[cnt].dis=z;
head[x]=cnt;
} inline int getint(){
int X=0;int w=0;char ch=0;
while(!isdigit(ch))w|=ch=='-',ch=getchar();
while( isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
} void dfs(int now){
sze[now]=1;
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(sze[to]) continue;
d[to]=d[now]+1;
dis[to]=dis[now]^edge[i].dis;cme[to]=edge[i].dis;
dfs(to);sze[now]+=sze[to];fa[to]=now;
if(sze[to]>sze[son[now]])
son[now]=to;
}
} void dfs2(int now,int low){
dfn[now]=++tot;fs[tot]=now;top[now]=low;
if(son[now])
dfs2(son[now],low);
for(int i=head[now];i;i=edge[i].nxt){
int to=edge[i].to;
if(dfn[to]) continue;
dfs2(to,to);
}
} void pushup(int cur){
sum[cur]=sum[cur<<1]+sum[cur<<1|1];
} void build(int cur,int l,int r){
if(l==r){
int now=dis[fs[l]];
for(int i=1;i<=10;i++){
if(now>>i-1&1)
sum[cur].a[i][1]++;
else sum[cur].a[i][0]++;
} return;
}
int mid=l+r>>1;
build(cur<<1,l,mid);build(cur<<1|1,mid+1,r);
pushup(cur);
} void pushdown(int cur){
if(!lazy[cur]) return;
for(int i=1;i<=10;i++){
if(lazy[cur]>>i-1&1){
swap(sum[cur<<1].a[i][0],sum[cur<<1].a[i][1]);
swap(sum[cur<<1|1].a[i][0],sum[cur<<1|1].a[i][1]);
}
}
lazy[cur<<1]^=lazy[cur];lazy[cur<<1|1]^=lazy[cur];lazy[cur]=0;
} Node query(int cur,int l,int r,int ql,int qr){
if(ql<=l and r<=qr)
return sum[cur];
int mid=l+r>>1;pushdown(cur);
Node z;memset(z.a,0,sizeof z.a);
if(ql<=mid)
z=z+query(ls);
if(mid<qr)
z=z+query(rs);
return z;
} int ask(int x,int y){
Node z;memset(z.a,0,sizeof z.a);
while(top[x]!=top[y]){
// printf("X=%lld,y=%lld\n",x,y);
if(d[top[x]]<d[top[y]])
swap(x,y);
z=z+query(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if(d[x]<d[y]) swap(x,y);
z=z+query(1,1,n,dfn[y],dfn[x]);
int ans=0;
for(int i=1;i<=10;i++)
ans+=(1<<i-1)*z.a[i][0]*z.a[i][1];
return ans;
} void modify(int cur,int l,int r,int ql,int qr,int z){
if(ql<=l and r<=qr){
for(int i=1;i<=10;i++){
if(z>>i-1&1)
swap(sum[cur].a[i][0],sum[cur].a[i][1]);
}
lazy[cur]^=z;return;
}
pushdown(cur);int mid=l+r>>1;
if(ql<=mid)
modify(ls,z);
if(mid<qr)
modify(rs,z);
pushup(cur);
} signed main(){
n=getint(),m=getint();
for(int i=1;i<n;i++){
int x=getint(),y=getint(),z=getint();
add(x,y,z);add(y,x,z);
}
d[1]=1;dfs(1);dfs2(1,1);build(1,1,n);
while(m--){
if(getint()==1){
int x=getint(),y=getint();
printf("%lld\n",ask(x,y));
} else{
int x=getint(),y=getint(),z=getint();
if(d[x]<d[y]) swap(x,y);
modify(1,1,n,dfn[x],dfn[x]+sze[x]-1,cme[x]^z);
cme[x]=z;
}
} return 0;
}

[Luogu 3401] 洛谷树的更多相关文章

  1. [洛谷P3401] 洛谷树

    洛谷题目连接:洛谷树 题目背景 萌哒的Created equal小仓鼠种了一棵洛谷树! (题目背景是辣鸡小仓鼠乱写的QAQ). 题目描述 树是一个无环.联通的无向图,由n个点和n-1条边构成.树上两个 ...

  2. 洛谷树剖模板题 P3384 | 树链剖分

    原题链接 对于以u为根的子树,后代节点的dfn显然比他的dfn大,我们可以记录一下回溯到u的dfn,显然这两个dfn构成了一个连续区间,代表u及u的子树 剩下的就和树剖一样了 #include< ...

  3. Luogu P1738 洛谷的文件夹

    P1738 Luogu 发一个链表题解! 仅有24ms,排名第一哦~ 圆圈代表点,每个店有两个指针,一个指向自己兄弟(同级文件夹),另一个指向自己孩子(子文件夹),还有一个保存当前名字. 有点像二叉树 ...

  4. 让lu哥头痛了许久的代码(洛谷:树的统计)

    错在单点修改时传的是a,应该是id[a](Line 89).谨记!!! //fushao zuishuai #include <cstdio> #include <cstring&g ...

  5. 洛谷P3655 差分数组 树状数组

    题目链接:https://www.luogu.org/problemnew/show/P3655 不一定对,仅供参考,不喜勿喷,不喜勿喷. 先copy洛谷P3368 [模板]树状数组 2 题解里面一位 ...

  6. 洛谷P4332 [SHOI2014]三叉神经树(LCT,树剖,二分查找,拓扑排序)

    洛谷题目传送门 你谷无题解于是来补一发 随便百度题解,发现了不少诸如树剖\(log^3\)LCT\(log^2\)的可怕描述...... 于是来想想怎么利用题目的性质,把复杂度降下来. 首先,每个点的 ...

  7. 洛谷P2922 [USACO008DEC] 秘密消息Secret Message [Trie树]

    洛谷传送门,BZOJ传送门 秘密消息Secret Message Description     贝茜正在领导奶牛们逃跑.为了联络,奶牛们互相发送秘密信息.     信息是二进制的,共有M(1≤M≤5 ...

  8. 【题解】洛谷P4145 花神游历各国(线段树)

    洛谷P4145:https://www.luogu.org/problemnew/show/P4145 思路 这道题的重点在于sqrt(1)=1 一个限制条件 与正常线段树不同的是区间修改为开方 那么 ...

  9. 【题解】洛谷P1198 [JSOI2008] 最大数(线段树)

    洛谷P1198:https://www.luogu.org/problemnew/show/P1198 思路 一道水水的线段树 20分钟A掉 这道题只涉及到单点修改和区间查询 所以这道题甚至不用Laz ...

随机推荐

  1. springboot开发流程

    public class User { private int id; private String username; private String password; private int ag ...

  2. python 常用知识点

    1,字典get用法 如果key没有值,返回一个None >>> dic = {'k1':'v1','k2':'v2','k3':'v3'} >>> dic.get( ...

  3. Java集合:HashSet的源码分析

    Java集合---HashSet的源码分析   一.  HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该 ...

  4. 我的idea突然没有SVN了是怎么回事

    总结一下没有svn选项的几种情况: 情况1:IntelliJ IDEA打开带SVN信息的项目不显示SVN信息,项目右键SVN以及图标还有Changes都不显示解决方法 在VCS菜单中有个开关,叫Ena ...

  5. MFC树形控件的使用(右键点击)

    在MFC中,会用到树形控件,这里做下记录. 右键点击 1.添加右键点击事件(NM_RCLICK) 2.获得鼠标在Client的坐标 CPoint point; GetCursorPos(&po ...

  6. python函数(一)

    python函数(一) 1.函数的定义: def test(): print('test is running...') return 定义一个函数,有3个部分需要注意: 函数名称.函数的命名规范与变 ...

  7. 学以致用二十三-----shell脚本里调用脚本

    当前脚本可以调用其他目录下的脚本,并可以直接使用其他脚本里的函数. 首先查看脚本目录 执行net_set.sh,同时执行colos.sh 并可直接使用 color.sh中的函数 net_set.sh ...

  8. Nginx unit 源码安装初体验

    Nginx unit 源码安装初体验 上次介绍了从yum的安装方法(https://www.cnblogs.com/wang-li/p/9684040.html),这次将介绍源码安装,目前最新版为1. ...

  9. FastDFS分布式文件系统配置文件详解

    一.tracker配置文件详解: # is this config file disabled# false for enabled# true for disableddisabled=false# ...

  10. 消息中间件——kafka

    1.1.1 什么是消息中间件 消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成.通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信.对 ...