光荣的梦想

Posted qswx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了光荣的梦想相关的知识,希望对你有一定的参考价值。

【Problem description】

Prince 对他在这片大陆上维护的秩序感到满意,于是决定启程离开艾泽拉斯。在他动身之前,Prince决定赋予King_Bette最强大的能量以守护世界、保卫这里的平衡与和谐。在那个时代,平衡是个梦想。因为有很多奇异的物种拥有各种不稳定的能量,平衡瞬间即被打破。KB决定求助于你,帮助他完成这个梦想。
  一串数列即表示一个世界的状态。
  平衡是指这串数列以升序排列。而从一串无序序列到有序序列需要通过交换数列中的元素来实现。KB的能量只能交换相邻两个数字。他想知道他最少需要交换几次就能使数列有序。

【Input format】

第1行为数列中数的个数n(n <= 100000)

第2行为n个数(绝对值不超过100000)。表示当前数列的状态。

【Output format】

输出一个整数,表示最少需要交换几次能达到平衡状态。

【Algorithm design】

线段树/排序二叉树

【Problem analysis】

因为有“只能交换相邻两个数字”这个条件,所以交换的总次数是数列中逆序对的个数。证明:在一个数列中,维护一个长度为i的从1到i的单调队列,每次i增长1,推入第i+1个数,为保证单调队列,i要进行当前自身的逆序对个数次交换。以此类推,交换的总次数为数列中逆序对的个数,证毕。

   线段树

求解逆序对的方法很多,可以处理前缀和,每次记录前i个数中每个数的个数,数字x的逆序对个数即为1~x-1的所有数个数之和,记录每个数的个数原本为O(n),可以用线段树优化为O(log2n)。因为极限数据下数字个数与数字大小差不多,所以觉得离散化没有必要,但是离散化可以避免负数的情况,值得优化。(虽然不离散化的也可以处理负数并且过了…)

线段树打完错了很多次,因为100000^2是个大整数却没有意识到..

 排序二叉树

同理啦。。

【Source code】

线段树(非离散)

#include <bits/stdc++.h>

#define ll long long

 

using namespace std;

 

int n,num;

ll ans;

 

struct node

{

      int l,r,add;

      ll s;

#define l(i) seg_tree[i].l

#define r(i) seg_tree[i].r

#define s(i) seg_tree[i].s

#define add(i) seg_tree[i].add

#define lkid sub<<1

#define rkid sub<<1|1

}seg_tree[800040];

 

void build_tree(int sub,int l,int r)

{

      l(sub)=l;

      r(sub)=r;

      s(sub)=0;

      if(l==r)

            return ;

      int mid=(l+r)>>1;

      build_tree(lkid,l,mid);

      build_tree(rkid,mid+1,r);

      return ;

}//建树函数

 

void push_down(int sub)

{

      if(!add(sub))

            return ;

      s(lkid)+=add(sub)*(r(lkid)-l(lkid)+1);

      s(rkid)+=add(sub)*(r(rkid)-l(rkid)+1);

      add(lkid)+=add(sub);

      add(rkid)+=add(sub);

      add(sub)=0;

}//扩散函数

 

void change(int sub,int l,int r)

{

      if(l(sub)>r||r(sub)<l)

            return ;

      if(l(sub)>=l&&r(sub)<=r)

      {

            s(sub)+=r(sub)-l(sub)+1;

            add(sub)++;

            return ;

      }

      push_down(sub);

      change(lkid,l,r);

      change(rkid,l,r);

      s(sub)=s(lkid)+s(rkid);

      return ;

}//修改区间

 

void query(int sub,int tar)

{

      if(l(sub)>tar||r(sub)<tar)

            return ;

      if(l(sub)==tar&&r(sub)==tar)

      {

            ans+=s(sub);

            return ;

      }

      push_down(sub);

      query(lkid,tar);

      query(rkid,tar);

      s(sub)=s(lkid)+s(rkid);

      return ;

}//查找单点

 

int main()

{

      freopen("dream.in","r",stdin);

      freopen("dream.out","w",stdout);

      cin>>n;

      build_tree(1,1,200010);

      for(int i=1;i<=n;i++)

      {

            cin>>num;

            num+=100001;

            change(1,1,num-1);

            query(1,num);

      }//加上100000防止负数

      cout<<ans<<endl;

      return 0;

}

 

线段树(离散)

#include <bits/stdc++.h>

#define ll long long

 

using namespace std;

 

int n,place[100010],num[100010];

ll ans;

 

pair <int,int> sortnum[100010];

 

struct node

{

      int l,r,add;

      ll s;

#define l(i) seg_tree[i].l

#define r(i) seg_tree[i].r

#define s(i) seg_tree[i].s

#define add(i) seg_tree[i].add

#define lkid sub<<1

#define rkid sub<<1|1

}seg_tree[400040];

 

void build_tree(int sub,int l,int r)

{

      l(sub)=l;

      r(sub)=r;

      s(sub)=0;

      if(l==r)

            return ;

      int mid=(l+r)>>1;

      build_tree(lkid,l,mid);

      build_tree(rkid,mid+1,r);

      return ;

}//建树函数

 

void push_down(int sub)

{

      if(!add(sub))

            return ;

      s(lkid)+=add(sub)*(r(lkid)-l(lkid)+1);

      s(rkid)+=add(sub)*(r(rkid)-l(rkid)+1);

      add(lkid)+=add(sub);

      add(rkid)+=add(sub);

      add(sub)=0;

}//标记下传函数

 

void change(int sub,int l,int r)

{

      if(l(sub)>r||r(sub)<l)

            return ;

      if(l(sub)>=l&&r(sub)<=r)

      {

            s(sub)+=r(sub)-l(sub)+1;

            add(sub)++;

            return ;

      }

      push_down(sub);

      change(lkid,l,r);

      change(rkid,l,r);

      s(sub)=s(lkid)+s(rkid);

      return ;

}//修改函数

 

int query(int sub,int tar)

{

      if(l(sub)>tar||r(sub)<tar)

            return 0;

      if(l(sub)==tar&&r(sub)==tar)

            return s(sub);

      push_down(sub);

      return query(lkid,tar)+query(rkid,tar);

}//查找函数

 

int main()

{

      freopen("dream.in","r",stdin);

      freopen("dream.out","w",stdout);

      cin>>n;

      build_tree(1,1,n);

      for(int i=1;i<=n;i++)

      {

            cin>>num[i];

            sortnum[i]=make_pair(num[i],i);

      }

      sort(sortnum+1,sortnum+n+1);

      for(int i=1;i<=n;i++)

            place[sortnum[i].second]=i;

//找到编号为i的数在升序中的位置

      for(int i=1;i<=n;i++)

      {

            change(1,1,place[i]-1);

            ans+=query(1,place[i]);

      }//加上逆序对个数

      cout<<ans<<endl;

      return 0;

}

 

排序二叉树:

#include <bits/stdc++.h>
#define ll long long

using namespace std;

int n;
ll ans;

struct node
{
    ll data;
    int l,r,sum;
}tree[100010];

void push_into(int sub)
{
    int cpr=1;
    while(1)
    {
        if(tree[sub].data<tree[cpr].data)
        {
            ans+=tree[cpr].sum;
            if(tree[cpr].l)
                cpr=tree[cpr].l;
            else
            {
                tree[cpr].l=sub;
                tree[sub].sum=1;
                return;
            }
        }
        else
        {
            tree[cpr].sum++;
            if(tree[cpr].r)
                cpr=tree[cpr].r;
            else
            {
                tree[cpr].r=sub;
                tree[sub].sum=1;
                return;
            }
        }    
    }
    return;
}

int main()
{
    freopen("dream.in","r",stdin);
    freopen("dream.out","w",stdout);
    cin>>n>>tree[1].data;
    tree[1].sum=1;
    for(int i=2;i<=n;i++)
    {
        cin>>tree[i].data;
        push_into(i);
    }
    cout<<ans<<endl;
    return 0;
}

































































以上是关于光荣的梦想的主要内容,如果未能解决你的问题,请参考以下文章

测试工程师的光荣与梦想

测试工程师的光荣与梦想

测试工程师的光荣与梦想

游戏测试工程师的光荣与梦想-百炼成钢

他89岁,拿下人生第3个博士学位,横跨医学物理学,只为“实现儿时梦想”

你有梦想吗?华为云学院助你实现梦想