洛谷P3521 [POI2011]ROT-Tree Rotations

链接

点击跳转

题解

这题据说是线段树合并板子题…

其实我觉得也不是特别的板,逆序对处理的地方还是要思考一下…(也可能是我太菜了)

整体做法是看一下交换子树和不交换子树的代价哪个更小。

问题就是怎么统计这个量呢?

网上的很多代码都是直接在线段树合并的同时去计算这两个量,但是我把线段树合并写成了一个结构体,原则上不太像改变封装内部的东西。所以我就每次只扫描较小的那一颗子树,查询另一棵对应的线段树中的一些信息,这样复杂度类似启发式合并,是 O ( n l o g n ) O(nlogn) O(nlogn)的。

代码

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 200010
#define cl(x) memset(x,0,sizeof(x))
#define rep(_,__) for(_=1;_<=(__);_++)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct joinable_segment_tree
{
    int sum[maxn*18*2], ch[maxn*18*2][2], tot;
    void clear(){tot=0;}
    int New(int v=0)
    {
        tot++;
        sum[tot]=v;
        ch[tot][0]=ch[tot][1]=0;
        return tot;
    }
    int create(int l, int r, int pos, int v)  //创建一条链
    {
        int ret=New(v), o=ret;
        while(l<r)
        {
            int mid(l+r>>1);
            if(pos<=mid)o=ch[o][0]=New(v), r=mid;
            else o=ch[o][1]=New(v), l=mid+1;
        }
        return ret;
    }
    int join(int u, int v)
    {
        if(!u or !v)return u|v;
        auto t=New(sum[u]+sum[v]);
        ch[t][0]=join(ch[u][0],ch[v][0]);
        ch[t][1]=join(ch[u][1],ch[v][1]);
        return t;
    }
    int qsum(int o, int l, int r, int L, int R) //在[L,R]里查[l,r]的和
    {
        int mid(L+R>>1), ans(0);
        if(l<=L and r>=R)return sum[o];
        if(l<=mid)ans+=qsum(ch[o][0],l,r,L,mid);
        if(r>mid)ans+=qsum(ch[o][1],l,r,mid+1,R);
        return ans;
    }
}mori;
ll ans=0;
int n, v[maxn], tot;
int dfs()
{
    int x=read();
    if(x)return mori.create(1,n,v[++tot]=x,+1);
    else
    {
        int now=tot, tot1, tot2;
        int t1, t2, ret;
        t1=dfs(); tot1=tot;
        t2=dfs(); tot2=tot;
        ret=mori.join(t1,t2);
        ll cnt1=0, cnt2=0;
        if(tot1-now<tot2-tot1)
            for(int i=now+1;i<=tot1;i++)
                cnt1+=mori.qsum(t2,1,v[i],1,n),
                cnt2+=mori.qsum(t2,v[i],n,1,n);
        else
            for(int i=tot1+1;i<=tot2;i++)
                cnt1+=mori.qsum(t1,1,v[i],1,n),
                cnt2+=mori.qsum(t1,v[i],n,1,n);
        ans += min(cnt1,cnt2);
        return ret;
    }
}
int main()
{
    n=read();
    dfs();
    cout<<ans;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值