题目描述

给定一棵n 个点的树,每条边上都有一个权值。现在按顺序删掉所有的n-1条边,每删掉一条边询问当前有多少条路径满足路径上所有边权值异或和为0。

输入输出格式

输入格式:

第一行一个整数n。

接下来n-1 行,每行三个整数ai,bi, zi,满足1<= ai, bi <=n,表示树上编号为ai 的点和编号为bi 的点中间连有一条权值为zi 的边。

接下来一行n-1 个整数,两两之间有一个空格隔开,表示一个1~ n- 1 的排列,表示n - 1 条边的删边顺序。

输出格式:

输出n 行,每行一个整数,依次表示删掉第0~ n - 1 条边之后的边权异或和为零的路径数。

输入输出样例

输入样例#1:

4
1 2 0
2 3 0
2 4 0
3 1 2
输出样例#1:

6
3
1
0

说明

对于20% 数据,满足n <= 1000。

对于另外30% 数据,满足所有的zi = 0。

对于全部数据,满足n <=10^5,0<= zi<= 10^9。

洛谷上多一些像这样质量高的原创题就好啦qwq(吐槽一下)

首先不考虑删边什么乱七八糟的,假如就是给你一棵树,问你多少对点之间的路径Xor和为零,该怎么求???

"我知道我知道,那不就是点分一下,经过当前重心的可以O(子树大小)求出(两个端点到根的路径Xor和相同就算一对),不经过的可以递归下去找,每一层还不用排序,总共O(N log N)就ojbk啦!"

"抱歉,其实dfs一遍就好了。。。。。"

由于Xor极其特殊的性质(Xor一个数两边相当于啥也没做),所以我们可以直接随便选个根然后一遍dfs统计Xor[](到根的路径异或和)一样的点对数就好啦,因为LCA以上的路径被异或了两次相当于没有异或。。。

有了这个较为简单的算法我们就很好去搞动态的情况了。

众所周知,加边比删边来的简单,于是我们可以反着做,考虑加入一条边会新产生多少点对路径Xor和为0.

当然,到了这还需要一点脑洞:我们用并查集去维护当前每个点所在的树根是哪个,并在树根上挂一个 unordered_map,记录一下这颗小树的所有点的Xor[] (当然只需要记 对于任意x , Xor[i] = x 的 i 有多少个就好啦)。合并的时候,因为两个联通分量的树根不一样,所以要暴力把小的树的Xor[]重新算一下(以大树树根为基准),并清空小树的unordered_map,然后把新算的Xor[]加入到大树树根的unordered_map中,最后就是并查集的合并了,别忘了在图中还要加边。。。不然以后没法dfs了233.

但是看起来好暴力啊?会不会超时啊???

但这是启发式合并啊,一颗大小为siz的树会对合并的时间复杂度贡献一个 siz 当且仅当它和一个 大小 >siz 的树合并了,得到一个大小 >2*siz 的新树,所以一个点最多贡献log N次合并的复杂度就到大树里啦,所以总的复杂度就是O(N log N)啦。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define pb push_back
const int maxn=100005;
unordered_map<int,int> mmp[maxn];
int n,m,Fa[maxn],p[maxn],u[maxn],v[maxn],w[maxn],S[maxn],T,tp;
int to[maxn*2],ne[maxn*2],hd[maxn],num,siz[maxn],Xor[maxn],val[maxn*2];
inline void add(int x,int y,int z){ to[++num]=y,ne[num]=hd[x],hd[x]=num,val[num]=z;}
int getFa(int x){ return Fa[x]==x?x:(Fa[x]=getFa(Fa[x]));}
ll ans[maxn],now; void dfs(int x,int fa){
S[++tp]=Xor[x],now+=(ll)mmp[T][Xor[x]];
for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa) Xor[to[i]]=Xor[x]^val[i],dfs(to[i],x);
} inline void Merge(int x,int y,int z){
int fa=getFa(x),fb=getFa(y);
if(siz[fa]>siz[fb]) swap(fa,fb),swap(x,y); mmp[fa].clear(),Fa[fa]=fb,siz[fb]+=siz[fa];
Xor[x]=Xor[y]^z,T=fb,dfs(x,y); for(;tp;tp--) mmp[T][S[tp]]++;
add(x,y,z),add(y,x,z);
} inline void solve(){
for(int i=n-1;i;i--){
Merge(u[p[i]],v[p[i]],w[p[i]]);
ans[n-i]=now;
}
} int main(){
scanf("%d",&n);
for(int i=1;i<n;i++) scanf("%d%d%d",u+i,v+i,w+i);
for(int i=1;i<n;i++) scanf("%d",p+i);
for(int i=1;i<=n;i++) siz[i]=1,Fa[i]=i,mmp[i][0]=1,Xor[i]=0; solve(); for(int i=n-1;i>=0;i--) printf("%lld\n",ans[i]);
return 0;
}

  

洛谷 P3359 改造异或树的更多相关文章

  1. 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

  2. [luogu3359]改造异或树

    [luogu3359]改造异或树 luogu 和之前某道题类似只有删边的话考虑倒着加边 但是怎么统计答案呢? 我们考虑以任意点为根dfs一遍求出每个点到根的路径异或和s[i] 这样任意两点x,y的路径 ...

  3. 洛谷p3384【模板】树链剖分题解

    洛谷p3384 [模板]树链剖分错误记录 首先感谢\(lfd\)在课上调了出来\(Orz\) \(1\).以后少写全局变量 \(2\).线段树递归的时候最好把左右区间一起传 \(3\).写\(dfs\ ...

  4. 洛谷P3688/uoj#291. [ZJOI2017]树状数组

    传送门(uoj) 传送门(洛谷) 这里是题解以及我的卡常数历程 话说后面那几组数据莫不是lxl出的这么毒 首先不难发现这个东西把查询前缀和变成了查询后缀和,结果就是查了\([l-1,r-1]\)的区间 ...

  5. 洛谷 P3384 【模板】树链剖分

    树链剖分 将一棵树的每个节点到它所有子节点中子树和(所包含的点的个数)最大的那个子节点的这条边标记为"重边". 将其他的边标记为"轻边". 若果一个非根节点的子 ...

  6. 洛谷P3459 [POI2007]MEG-Megalopolis(树链剖分,Splay)

    洛谷题目传送门 正解是树状数组维护dfn序上的前缀和,这样的思路真是又玄学又令我惊叹( 我太弱啦,根本想不到)Orz各路Dalao 今天考了这道题,数据范围还比洛谷的小,只有\(10^5\)(害我复制 ...

  7. [洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?

    其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数 ...

  8. 点分治模板(洛谷P4178 Tree)(树分治,树的重心,容斥原理)

    推荐YCB的总结 推荐你谷ysn等巨佬的详细题解 大致流程-- dfs求出当前树的重心 对当前树内经过重心的路径统计答案(一条路径由两条由重心到其它点的子路径合并而成) 容斥减去不合法情况(两条子路径 ...

  9. 洛谷P2617 Dynamic Rankings (主席树)

    洛谷P2617 Dynamic Rankings 题目描述 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a ...

随机推荐

  1. Android设为系统默认的短信应用

    要设为系统默认的短信应用首先要配置一下AndroidManifest.xml文件,添加下列: <!-- BroadcastReceiver that listens for incoming S ...

  2. Android 适配器 自定义

    前言:最近看了几个开源项目,发现适配器这东西用的很多,一开始觉得这东西高大上,其实呢,感觉就是一个中转站,或者说是一个接口工具,将数据填充到一个视图中,几乎任何项目都会涉及到.所以今天也简单看了一下, ...

  3. document.domain跨子域

    document.domain 用来得到当前网页的域名.比如在地址栏里输入: javascript:alert(document.domain); //www.315ta.com 我们也可以给docu ...

  4. leetcode 【 Plus One 】python 实现

    题目: Given a non-negative number represented as an array of digits, plus one to the number. The digit ...

  5. Python+Selenium中级篇之-封装一个自己的类-浏览器引擎类

    前一篇文章我们知道了,如何去封装几个简单的Selenium方法到我们自定义的类,这次我们编写一个类,叫浏览器引擎类,通过更改一个字符串的值,利用if语句去判断和控制启动那个浏览器.这里我们暂时,支持三 ...

  6. Linux下python升级到python-2.7.13

    下载python最新版本2.7.13并编译安装 wget https://www.python.org/ftp/python/2.7.12/Python-2.7.13.tar.xz xz -d Pyt ...

  7. Spring boot 整合jsp、thymeleaf、freemarker

    1.创建spring boot 项目 2.pom文件配置如下: <dependencies> <dependency> <groupId>org.springfra ...

  8. 精通CSS高级Web标准解决方案(7、布局)

    7.1 让设计居中 7.1.1 使用自动空白边让设计居中 <body> <div id="wrapper"> </div> </body& ...

  9. 【转】Unity3D 入门小技巧——鼠标拾取并移动物体

    http://blog.csdn.net/sysujackjiao/article/details/69396274 一.鼠标拾取物体的原理 在Unity3D当中,想要在观察面(Aspect)中拾取物 ...

  10. C#中的is和as的转型区别

    摘自CLR via C#第三版第四章 在c#中is可以用来判断一个对象是否兼容给定的类型,如果是返回true,否则返回false. 同时is是永不会抛出异常的.如果对象引用是null,is操作符总是返 ...