洛谷 P3359 改造异或树

时间:2021-06-06 21:33:51

题目描述

给定一棵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;
}