光荣的梦想
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;
}
以上是关于光荣的梦想的主要内容,如果未能解决你的问题,请参考以下文章