给定有根树 带点权 求出满足a[i] ^ a[j]=a[lca]
的点 i^j
之和 。 点权<=1e6
我们考虑枚举根节点rt,求出当rt作为LCA时产生的贡献,相当于是枚举了LCA。
从而把问题转化成了子树查询问题,对于rt,能产生贡献的点对必顶位于rt不同孩子子树中,所以我们考虑dsu on tree,vector<int> num[i]
表示点权为i的节点信息,最后保留重儿子信息。 对于每个轻儿子,先统计贡献,再更新num数组。
由于某棵子树删除影响后,vector<int> num[i]
其实是空的状态,所以可以直接递归+pop_back 。
统计贡献直接枚举暴力统计…感觉时间复杂度很假,但是450ms就过了。也许是数据水了吧。
插言,已经是第二次遇到dsu on tree中枚举LCA作为根节点,计算孩子子树答案,计算完了再维护相关信息了。值得一提的是,这样的写法可能有坑,那就是当前根节点也许会产生贡献,这个要在他upd之前计算,虽然这题的根节点没有产生贡献,因为点权不会为0,异或结果不可能为LCA。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
#define pdd pair<double, double>
#define re register
#define lc rt<<1
#define rc rt<<1|1
const int maxn = 1e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
//给定有根树 带点权 求出满足a[i]^a[j]=a[lca]的点 i^j 之和
//转化成每个子树中查询答案 累加起来 (树根就是LCA 也就相当于枚举LCA了)
//对于节点u 枚举num[a[lca]^a[u]]的每个节点 暴力统计
vector<int> num[maxn*20];
vector<int> g[maxn];
int n;
int a[maxn];
int in[maxn],pos[maxn],clk,son[maxn],siz[maxn],dep[maxn];
void dfs1(int rt,int fa)
{
dep[rt]=dep[fa]+1;
siz[rt]=1;
in[rt]=++clk;
pos[clk]=rt;
for(int i:g[rt])
{
if(i==fa) continue;
dfs1(i,rt);
siz