2024杭电多校1003树题解

在这里插入图片描述
题意:
要求我们维护子树的信息,将所求答案进行异或
思路:
首先我们需要前缀知识(树上启发式合并)。题目上的公式可以化为max* max-max*min。考虑将一个点加到当前集合中,我们可以利用树状数组来快速维护这个信息,同时还可以进行单点修改。考虑如何在树上进行这个操作,应该先计算轻子树,然后再计算重子树,然后将u和轻子树的信息与重子树进行合并。在计算过程中,我们始终要保留重子树的信息,合并完成后,要将轻子树的信息进行清空,因为本身就是一个迭代的过程,每一个轻子树都是相互独立的,相当于我们把轻子树的信息合并到重子树上去,整个过程使用一个树状数组,所以要清空。时间复杂度O(nlog²)
代码实现

// #pragma GCC optimize(3,"Ofast","inline")
// #pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define all(v) v.begin(), v.end()
#define point(n) fixed << setprecision(n)
#define IOS                      \
    ios::sync_with_stdio(false); \
    cin.tie(0), cout.tie(0);
#define endl '\n'
#define inf 0x3f3f3f3f
typedef  long  long ll;
typedef unsigned  long  long ull;
typedef pair<ll, ll> PII;
typedef pair<ll, array<ll, 2>> PIA;
const int N = 1e6 + 10,mod=1e9+7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
int n, m, k;
int f[N];
vector<int >h[N];
int sz[N],son[N];
ull cnt;
template<class T>
struct BIT {
  vector<T> c;
    int size;
    void init(int s) {
        size = s;
        c.resize(size + 1, 0);
    }
    T query(int x) { // 1 ... x
        assert(x <= size);
        T s = 0;
        for (; x; x -= x & (-x)) {
            s += c[x];
        }
        return s;
    }
 
    void modify(int x, T s) {
        assert(x != 0);
        for (; x <= size; x += x & (-x)) {
            c[x] += s;
        }
    }
};
BIT<ull>w1,w2,w3;
ull ans[N];
void dfs_son(int u,int fa)
{
    sz[u]=1;
    for(auto g:h[u]){
        if(g==fa)continue;
        dfs_son(g,u);
        if(sz[g]>sz[son[u]]){
            son[u]=g;
        }
        sz[u]+=sz[g];
    }
}
void get1(int u,int fa,int pson,ull sign)
{
    // max*max-max*min 分两种情况,大于f[u]和小于等于f[u],直接利用题目的公式进行计算
    cnt+=((w1.query(1e6)-w1.query(f[u])+w3.query(f[u])*f[u]*f[u])-w2.query(1e6)*f[u])*2;
    w1.modify(f[u],sign*f[u]*f[u]);//维护平方和
    w2.modify(f[u],sign*f[u]);//维护前缀和
    w3.modify(f[u],sign);//维护个数
    for(auto g:h[u]){
        if(g==fa||g==pson)continue;
        get1(g,u,pson,sign);
    }
 
}
void dfs(int u,int fa,int op)
{
    for(auto g:h[u]){
        if(g==fa||g==son[u])continue;
        dfs(g,u,0);
    }
    if(son[u])dfs(son[u],u,1);//求解重儿子信息
    get1(u,fa,son[u],1);//计算贡献,将u和轻子树的信息与重子树进行合并
    
    ans[u]=cnt;
    if(!op)get1(u,fa,0,-1),cnt=0;//将当前子树的信息进行清空。
 
}
void solve()
{
    cin >> n;
    w1.init(1e6+10);
     w2.init(1e6+10);
     w3.init(1e6+10);
    for(int i=1;i<n;i++){
        int a,b;
        cin >> a >> b;
        h[a].push_back(b);
        h[b].push_back(a);
    }
    for(int i=1;i<=n;i++){
        cin >> f[i];
    }
    dfs_son(1,-1);//求重儿子
    dfs(1,-1,1);
    ull res=0;
    for(int i=1;i<=n;i++){
        res^=ans[i];
     //   cout<<ans[i]<<" \n"[i==n];
    }
    cout<<res<<endl;
}
int  main()
{
    IOS;
    int _ = 1;
  //  cin >> _;
    while (_--)
    {
        solve();
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Radon.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值