Jackiesteed

www.github.com/jackiesteed

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

通过一个case解释算法过程.

比如 case :

5 9

第一步 ,序列为 1 2 3 4 5, 逆序数为 9

输出序列中第4个数4,因为 4后面有3个比4小的,而且删掉4后最大还可以得到6个逆序(3+6>=9),所以4符合条件,5也符合,但是5比4大,

3或更小不符合.

第二步,序列为1 2 3 5, 逆序数为9-3=6

输出序列中第4个数5,计算同上.

...

以上每一步计算出应该输出序列中第几个数可以O(1)时间实现,但是对于计算序列中第n个数是几,实现为O(N),N=50000超时,使用线段树

 可以在O(logN)时间内计算出来,程序的时间复杂度为O(N*logN),得解.

源代码:

View Code
//使用线段树解逆序数问题
//线段树功能是,在O(logN)的时间内在一个链表中
//找出第n个元素,
//同时可解约瑟夫环问题,如 POJ_3750
#include <iostream>
#include
<fstream>
#include
<algorithm>
#include
<cstring>
#include
<climits>
#include
<cstdio>
#include
<cmath>

using namespace std;

typedef
long long LL;

typedef
struct node
{
LL left, right, mid;
LL cnt;
//记录本区间内还有几个剩余
inline LL len()//区间没有删掉元素时的长度
{
return right - left + 1;
}
}node;

node tree[
1000000];

LL N,M;

void create(LL left, LL right, LL x)
{
tree[x].left
= left, tree[x].right = right;
tree[x].cnt
= right - left + 1;
tree[x].mid
= (left + right) / 2;
if (left == right)
return ;

create(left, tree[x].mid, x
* 2);
create(tree[x].mid
+ 1, right, x * 2 + 1);
}

LL
get(LL s, LL x)//计算对应相应的s,在区间内第s个数是几
{
if (tree[x].cnt == tree[x].len())
{
return tree[x].left + s - 1;
}
if (tree[x * 2].cnt >= s)
return get(s, x * 2);

return get(s - tree[x * 2].cnt, 2 * x + 1);
}

void del(LL s,LL x)//在区间内删掉s
{
tree[x].cnt
--;
if (tree[x].len() == 1)
return ;
if (s <= tree[x].mid)
del(s, x
* 2);
else
del(s,x
* 2 + 1);
}

LL calc(LL x, LL y)
//注意使用long long
{
LL res
= y - (x - 1) * (x - 2) / 2;
if(res < 0)
return 1;
return res + 1;
}

int main()
{
// freopen("input.txt", "r", stdin);
while(scanf("%I64d %I64d", &N, &M) != EOF)
{
if(-1 == N && -1 == M)
break;
LL x, y;
create(
1, N, 1);
for(LL i = 0; i < N; i++)
{
x
=calc(N - i,M);
y
=get(x, 1);
if(i)
putchar(
' ');
printf(
"%I64d",y);
del(y,
1);
M
-= x - 1;
}
putchar(
'\n');
}

return 0;
}

posted on 2011-05-27 10:08  Jackiesteed  阅读(750)  评论(0编辑  收藏  举报