树上动态插点 ---- F. Imbalance Value of a Tree(树上动态插点 + 并查集)

题目链接


题目大意:

定义 I ( x , y ) 为 x 到 y 路 径 上 最 大 值 和 最 小 值 之 差 I(x,y)为x到y路径上最大值和最小值之差 I(x,y)xy
现在叫你求 ∑ i = 1 n ∑ j = i n I ( x , y ) \sum_{i=1}^ {n}\sum_{j=i}^nI(x,y) i=1nj=inI(x,y)


解题思路:

  1. 首先我们可以这么看对于一个点我们可以去看看假设这个点是路径上面的最大值,有多少条路径?然后我们再去看看这个点是多少路径上的最小值贡献相减就可以了!!
  2. 但是怎么求?我们可以把点按照权值进行升序排序,假设我们先把树给清空然后点一个个插进行当插进一个新的点的时候,我们发现这个树里面的点权值都比你现在插进来的点小!!
  3. 然后插入的点我们去访问周围的点,去统计路径就可以了
  4. 路径分两种:1.是一个块里面的路径,2是两个块直接的路径

AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = N;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {
   read(first);
   read(args...);
}

inline void write(__int128 x) {
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)
        write(x/10);
    putchar(x%10+'0');
}

ll sum[2];
vector<int> G[maxn];
struct node {
    ll val;
    int id;
}arr[maxn];
bool cmpup(node a, node b) {
    return a.val < b.val;
}

bool cmpdown(node a, node b) {
    return a.val > b.val;
}
int n;

struct dsu {
    int fa[maxn], siz[maxn];
    bool vis[maxn];
    inline void init(int n) {
        for(int i = 1; i <= n; ++ i) vis[i] = 0, fa[i] = i, siz[i] = 1;
    }
    inline int find(int x) {
        return x == fa[x] ? x : fa[x] = find(fa[x]);
    }
    inline void Union(int u, int v) {
        int fu = find(u), fv = find(v);
        if(fu == fv) return;
        fa[fu] = fv;
        siz[fv] += siz[fu];
    }
}dsu;

void work(int idx) {
    for(int i = 1; i <= n; ++ i) {
        int id = arr[i].id;
        ll ans = 0;
        ll pre = 0;
        for(auto it : G[id]) {
            int fit = dsu.find(it);
            // cout << "it = " << it << " " << fit << " " << dsu.siz[fit] << endl;
            if(!dsu.vis[it]) continue;// 访问周围的点
            if(pre == 0) {
                pre = dsu.siz[fit]; 
            } else {
                ans += dsu.siz[fit] * pre;
                pre += dsu.siz[fit];
            }
        }
        for(auto it : G[id]) if(dsu.vis[it]) dsu.Union(it,id); //合并点
        dsu.vis[id] = 1;
        sum[idx] += (ans + pre) * arr[i].val;
        // cout << sum[idx] << endl;
    }
}

int main() {
    IOS;
    cin >> n;
    for(int i = 1; i <= n; ++ i) cin >> arr[i].val,arr[i].id = i;
    for(int i = 2; i <= n; ++ i) {
        int u, v;
        cin >> u >> v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    sort(arr+1,arr+1+n,cmpup);
    dsu.init(n);
    
    work(0);
    // cout << "---\n";
    // cout << sum[0] << endl;
    sort(arr+1,arr+1+n,cmpdown);
    dsu.init(n);
    work(1);
    cout << sum[0] - sum[1];
    // write(sum[0] - sum[1]);
}
/*
5
1 1 1 2 2
1 2
3 4
5 2
3 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值