ZOJ 4053 ICPC2018青岛网络赛 Couleur(可持久化线段树 + 复杂度分析)

版权声明:本文为博主原创文章,转载请著名出处 http://blog.csdn.net/u013534123 https://blog.csdn.net/u013534123/article/details/82762396

大致题意:给你一个序列,每次删掉序列中的一个数字,使得原本的一段变成两段。每次在删除之前,输出所有段中,逆序对数量最多的逆序对,强制在线。

对于这道题,你需要有一定的胆量。

对于逆序对,如果暴力求的话,其复杂度是O(NlogN)的,这个用很多中方式都可以求出来。现在考虑按顺序删掉数字,每次删掉数字之后,拆成两个新的区间,这样在所有区间中,逆序对最多是多少。考虑,删除之后,会产生两个新的区间,如果直接暴力重新算,显然会超时,所以可以利用一些原有的性质。

一个区间拆成两个之后,原本的大区间逆序对可以分为三个部分。一是左区间的逆序对个数;二是右区间的逆序对数;三是横跨两个区间的逆序对数目。我们大胆尝试,暴力去求小区间的逆序对数目,复杂度为O(Len log Len)。然后对于大的区间,我们考虑用总的逆序对,减去小区间逆序对,再减去横跨两个区间的逆序对。对于横跨两个区间的逆序对,我们可以遍历小的区间,然后查找大的区间中比枚举数字小(大)的数字个数。如此一次的复杂度就是O(smalen log biglen)。

我们再来考虑一下这个复杂度计算。我们的最后目标是把所有的区间分成单位区间,那么,如果把分割画到同一层,最后可以变成类似线段树的结构。第i层的区间数是2^{i-1}。可以证明,最坏的情况是每一次都是平均分,那么对于平均分每一层时间复杂度就是O(NlogN),由于树总共右logN层,所以复杂度就是O(NlogNlogN),两个logN的复杂度做出这题足矣。

具体做法,需要求一个区间内大于某个数字或者小于某个数字的个数,这个可以用主席树来做。但是实际上,注意到,处理每一个区间的时间是NlogN的,所以用很多方法都可以做,最简单的就是用pbds的红黑树,但是常数可能会大一些。具体见代码:

#include<bits/stdc++.h>
#define mod 256
#define LL long long
#define pb push_back
#define lb lower_bound
#define ub upper_bound
#define INF 0x3f3f3f3f
#define sf(x) scanf("%d",&x)
#define sc(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define clr(x,n) memset(x,0,sizeof(x[0])*(n+5))
#define file(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
using namespace std;

const int N = 100010;

int rt[N],a[N];

struct Persistent_SegTree
{
    struct node{int l,r,sum;} T[N<<5];
    int cnt; void init(){cnt=0;T[0]={0,0,0};}

    void ins(int &i,int old,int l,int r,int x)
    {
        i=++cnt;
        T[i]=T[old];
        T[i].sum++;
        if (l==r) return;
        int mid=(l+r)>>1;
        if (x<=mid) ins(T[i].l,T[old].l,l,mid,x);
               else ins(T[i].r,T[old].r,mid+1,r,x);
    }

    int query(int i,int j,int x,int l,int r)
    {
        if (l==r) return 0;
        if (T[i].sum==T[j].sum) return 0;
        int mid=(l+r)>>1;
        int tmp=T[T[i].l].sum-T[T[j].l].sum;
        if (mid>=x) return query(T[i].l,T[j].l,x,l,mid);
            else return tmp+query(T[i].r,T[j].r,x,mid+1,r);
    }

} Persist;

unordered_map<int,LL> mp;
multiset<LL> st;
set<int> ss;
int mx;

int main()
{
    int T; sf(T);
    while(T--)
    {
        LL ans=0; mx=0;
        int n; sf(n);
        st.clear(); ss.clear();
        mp.clear(); Persist.init();
        ss.insert(0); ss.insert(n+1);
        for(int i=1;i<=n;i++)
        {sf(a[i]); mx=max(mx,a[i]);}
        mx++;
        for(int i=1;i<=n;i++)
        {
            ans+=(i-1)-Persist.query(rt[i-1],rt[0],a[i]+1,1,mx);
            Persist.ins(rt[i]=0,rt[i-1],1,mx,a[i]);
        }
        st.insert(ans); mp[0]=ans;
        for(int i=1;i<=n;i++)
        {
            auto pp=st.end();
            pp--; ans=*pp;
            if (i!=n) printf("%lld ",ans);
                else printf("%lld\n",ans);
            int x; sf(x);
            x^=ans; auto pos=ss.ub(x);
            int l,r=*pos; pos--; l=*pos;
            st.erase(st.lb(mp[l]));
            LL tmp=0,temp=0,tt=mp[l];
            if (x-l>r-x)
            {
                for(int j=x+1;j<r;j++)
                    tmp+=x-l-Persist.query(rt[x],rt[l],a[j]+1,1,mx);
                for(int j=x+1;j<r-1;j++)
                    temp+=Persist.query(rt[r-1],rt[j],a[j],1,mx);
                mp[x]=temp; st.insert(temp);
                int xx=x-1-l-Persist.query(rt[x-1],rt[l],a[x]+1,1,mx);
                mp[l]=tt-tmp-temp-xx; st.insert(tt-tmp-temp-xx);
                ss.insert(x);
            } else
            {
                for(int j=l+1;j<x;j++)
                    tmp+=Persist.query(rt[r-1],rt[x-1],a[j],1,mx);
                for(int j=l+1;j<x-1;j++)
                    temp+=Persist.query(rt[x-1],rt[j],a[j],1,mx);
                mp[l]=temp; st.insert(temp);
                int xx=Persist.query(rt[r-1],rt[x],a[x],1,mx);
                mp[x]=tt-tmp-temp-xx; st.insert(tt-tmp-temp-xx);
                ss.insert(x);
            }
        }
    }
    return 0;
}


猜你喜欢

转载自blog.csdn.net/u013534123/article/details/82762396