ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P3521 [POI2011]ROT-Tree Rotations 题解

2022-04-17 19:01:34  阅读:151  来源: 互联网

标签:fir p2 p1 P3521 int 题解 Rotations Edge sec


一道线段树合并的题。

首先我们发现,如果我们交换了两棵子树,影响到的逆序对数量只会是这两棵子树交换之后数列改变的逆序对数量,对前面的数列和后面的数列并没有影响,对这两棵子树内部也没有影响,因为逆序对只关注相对位置及数的大小。

据此我们先建树,然后对于每个点整一棵值域线段树维护子树内的数,这个过程可以利用线段树合并完成,然后在合并的时候我们是需要统计逆序对数量的,设两个值 \(fir,sec\),\(fir\) 表示不换的逆序对数量,\(sec\) 表示换的逆序对数量。

接下来设 \(p1,p2\) 是待合并的两棵树,\(l(p1/p2),r(p1/p2)\) 是左右儿子,\(sum\) 是子树大小,\([ql,qr]\) 是区间,\(mid\) 是中点。

那么对于一个区间 \([ql,qr]\),显然我们换了之后逆序对数量会加上 \(r(p2).sum \times l(p1).sum\),不换会加上 \(r(p1).sum \times l(p2).sum\),但是这样子只会计算 \([ql,mid]\) 对于 \([mid+1,qr]\) 的逆序对,因此我们每次合并的时候都需要计算一次。

最后每合并完一棵树,我们都取 \(\min\{u,v\}\) 加到 \(ans\) 里面,做完了。

这道题有一个坑点就是说如果你不写空间回收,像我一样直接动态开点的话空间一定要开大点,不然第一个点会 RE。

GitHub:CodeBase-of-Plozia

Code:

/*
========= Plozia =========
    Author:Plozia
    Problem:P3521 [POI2011]ROT-Tree Rotations
    Date:2021/12/9
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 6e5 + 5;
int n, Head[MAXN], cnt_Edge = 1, Root[MAXN], r, cnt_SgT, cnt_Node;
LL ans, fir, sec;
struct node { int To, Next; } Edge[MAXN << 1];
struct SgT
{
    int l, r; LL sum;
    #define l(p) tree[p].l
    #define r(p) tree[p].r
    #define s(p) tree[p].sum
}tree[7000005];
// 就是这个地方,我开成 6000005 过不去,7000005 就过了

int Read()
{
    int sum = 0, fh = 1; char ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
    return sum * fh;
}
LL Max(LL fir, LL sec) { return (fir > sec) ? fir : sec; }
LL Min(LL fir, LL sec) { return (fir < sec) ? fir : sec; }
void add_Edge(int x, int y) { ++cnt_Edge; Edge[cnt_Edge] = (node){y, Head[x]}; Head[x] = cnt_Edge; }

int Read_Tree()
{
    int t = Read();
    if (t != 0) return t;
    else
    {
        t = ++cnt_Node;
        add_Edge(t, Read_Tree());
        add_Edge(t, Read_Tree());
        return t;
    }
}

void Insert(int &p, int x, int l, int r)
{
    if (!p) p = ++cnt_SgT;
    if (l == r) { ++s(p); return ; }
    int mid = (l + r) >> 1;
    if (x <= mid) Insert(l(p), x, l, mid);
    else Insert(r(p), x, mid + 1, r);
    s(p) = s(l(p)) + s(r(p)); return ;
}

int Merge(int p1, int p2, int l, int r)
{
    if (!p1 || !p2) return p1 + p2;
    int p = ++cnt_SgT;
    if (l == r) { s(p) = s(p1) + s(p2); return p; }
    int mid = (l + r) >> 1;
    fir = (fir + s(r(p1)) * s(l(p2)));
    sec = (sec + s(l(p1)) * s(r(p2))); // 注意每次都要计算!
    l(p) = Merge(l(p1), l(p2), l, mid);
    r(p) = Merge(r(p1), r(p2), mid + 1, r);
    s(p) = s(l(p)) + s(r(p)); return p;
}

void dfs(int now, int father)
{
    for (int i = Head[now]; i; i = Edge[i].Next)
    {
        int u = Edge[i].To;
        if (u == father) continue ;
        dfs(u, now); fir = sec = 0;
        Root[now] = Merge(Root[now], Root[u], 0, n);
        ans += Min(fir, sec);
    }
}

int main()
{
    cnt_Node = n = Read(); r = Read_Tree();
    if (n == 1) { puts("0"); return 0; }
    for (int i = 1; i <= n; ++i) Insert(Root[i], i, 0, n);
    dfs(r, r); printf("%lld\n", ans); return 0;
}

标签:fir,p2,p1,P3521,int,题解,Rotations,Edge,sec
来源: https://www.cnblogs.com/Plozia/p/16156782.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有