P3521 [POI2011]ROT-Tree Rotations

P3521 [POI2011]ROT-Tree Rotations

分析

我们看操作,我们可以将任意一个节点的左右子树交换。

则,我们是改变的一段序列的顺序。

这里类似于的归并排序求逆序对的过程。对于某一个节点,我们在考虑交换与否左右子树会产生的最少新逆序对是多少时,只与左右子树中的数字有关,而与其数字的具体的相对关系无关了。

例如,某节点的左子树为1,4,右子树3,2,其实右子树的3,2我们根本不关心,我们只在乎其中有哪些数字,左子树1,4,右子树2,3。则不交换产生的新的逆序对为2,交换了之后,也是2

我们直接从叶节点上升过程中,维护一个权值线段树,其中维护区间内有的值的数量和,直接进行线段树合并。

但是合并过程中,我们需要计算,不交换时产生的新的逆序对ans1交换时产生的新的逆序对ans2。在合并过程中,我们需要维护两个值cnt1/2,为对于当前区间[l,r]而言,左/右子树有多少个点在该区间前,即为左/右子树在[1,l-1]有多少个点。

落实到具体的合并操作中即为。

  1. 某个区间只有左子树有,而右子树没有,则ans1+=tr[u].cnt*cnt2
  2. 同理,某个区间只有右子树有,而左子树没有,则ans2+=tr[v].cnt*cnt1
  3. 接下来到叶节点后,我们直接两个都加,ans2+=tr[v].cnt*cnt1ans1+=tr[u].cnt*cnt2

其中有个小错误,坑了我一手。就是我们合并的时候,通常是直接将第二个线段树合并到第一个线段树上。但是这样会影响我们的参数传递,因为有时左子树已经变了。可能说的不是太清楚,直接看代码。

int merge(int u,int v,int l,int r,int cnt1,int cnt2)
{
    // cout<<l<<" "<<r<<" "<<cnt1<<" "<<cnt2<<endl;
    if(!u)
    {
        ans2 += 1ll*tr[v].cnt*cnt1;
        return v;
    }
    if(!v)
    {
        ans1 += 1ll*tr[u].cnt*cnt2;
        return u;
    }
    if(l==r)
    {
        ans1 += 1ll*tr[u].cnt*cnt2;ans2 += 1ll*tr[v].cnt*cnt1;
        tr[u].cnt += tr[v].cnt;
        return u;
    }
    int mid = l + r >> 1;
    tr[u].r = merge(tr[u].r,tr[v].r,mid+1,r,cnt1 + tr[tr[u].l].cnt,cnt2 + tr[tr[v].l].cnt);//就是这里,如果跟下面的反过来的话,则传入的参数就错了
    tr[u].l = merge(tr[u].l,tr[v].l,l,mid,cnt1,cnt2);
    pushup(u);
    return u;
}

AC_code

#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
using ll = long long;
const int N = 2e5 + 10;

struct Node
{
    int l,r;
    int cnt;
}tr[N*30];
int n,idx,cnt;
ll ans,ans1,ans2;
int root[N*2];

void pushup(int u)
{
    tr[u].cnt = tr[tr[u].l].cnt + tr[tr[u].r].cnt;
}

void modify(int &u,int l,int r,int x)
{
    if(!u) u = ++idx;
    if(l==r)
    {
        tr[u].cnt ++;
        return ;
    }
    int mid = l + r >> 1;
    if(x<=mid) modify(tr[u].l,l,mid,x);
    else modify(tr[u].r,mid+1,r,x);
    pushup(u);
}

int merge(int u,int v,int l,int r,int cnt1,int cnt2)
{
    // cout<<l<<" "<<r<<" "<<cnt1<<" "<<cnt2<<endl;
    if(!u)
    {
        ans2 += 1ll*tr[v].cnt*cnt1;
        return v;
    }
    if(!v)
    {
        ans1 += 1ll*tr[u].cnt*cnt2;
        return u;
    }
    if(l==r)
    {
        ans1 += 1ll*tr[u].cnt*cnt2;ans2 += 1ll*tr[v].cnt*cnt1;
        tr[u].cnt += tr[v].cnt;
        return u;
    }
    int mid = l + r >> 1;
    tr[u].r = merge(tr[u].r,tr[v].r,mid+1,r,cnt1 + tr[tr[u].l].cnt,cnt2 + tr[tr[v].l].cnt);
    tr[u].l = merge(tr[u].l,tr[v].l,l,mid,cnt1,cnt2);
    pushup(u);
    return u;
}

void dfs(int u)
{
    int x;cin>>x;
    if(!x) 
    {
        int l = ++cnt;dfs(l);
        int r = ++cnt;dfs(r);
        ans1 = 0,ans2 = 0;
        root[u] = merge(root[l],root[r],1,n,0,0);
        // cout<<ans1<<" "<<ans2<<'\n';
        ans += min(ans1,ans2);
    }
    else modify(root[u],1,n,x);
}

int main()
{
    ios;
    cin>>n;
    cnt = 1;
    dfs(cnt);
    cout<<ans<<'\n';
    return 0;
}
posted @ 2022-11-05 16:46  艾特玖  阅读(29)  评论(0)    收藏  举报