POJ 2085 treap O(nlogn) 与 贪心 O(n)算法

/* 问题是找出逆序数为m的最小n全排列
 * 直接暴过去找出第i轮需要第k小数,k = m-(n-1)*(n-2)/2+1 ,当然如果m比较小就k=1了 
 * 然后用treap找第k小数就可以了。。
 * O(nlogn)
 * 听说有O(n)的规律。。其实我做这题主要为了找规律。。。结果就这么被我水了过去。。
 * 好吧继续找规律
 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int result[50001], r;
#define MX 100000

int size, root;
long long n, m;

struct Node
{
    int l, r, key, rand_fix;
    int countl, countr;
}node[MX];
//left  rotate
void l_rot(int &index)
{
   int y = node[index].r;
   node[index].r = node[y].l;
   node[index].countr = node[y].countl;
   node[y].l = index;
   node[y].countl = node[index].countl + node[index].countr + 1;
   index = y;
}
//right rotate
void r_rot(int &index)
{
   int y = node[index].l;
   node[index].l = node[y].r;
   node[index].countl = node[y].countr;
   node[y].r = index;
   node[y].countr = node[index].countl + node[index].countr + 1;
   index = y;
}
void insert(int &index, int nkey)
{
    if(index == -1)
    {
        index = ++size;
        node[index].l = node[index].r = -1;
        node[index].rand_fix = rand();
        node[index].key = nkey;
        node[index].countl = node[index].countr = 0;
        return ;
    }
    if(nkey < node[index].key)
    {
        node[index].countl++;
        insert(node[index].l, nkey);
        if(node[node[index].l].rand_fix > node[index].rand_fix)
            r_rot(index);
    }
    else
    {
        node[index].countr++;
        insert(node[index].r, nkey);
        if(node[node[index].r].rand_fix > node[index].rand_fix)
            l_rot(index);
    }
}

bool remove(int &index, int nkey)
{
   if(index == -1) return false;
   if(nkey < node[index].key)
   {
       node[index].countl--;
       return remove(node[index].l, nkey);
   }
   else if(nkey > node[index].key)
   {
       node[index].countr--;
       return remove(node[index].r, nkey);
   }
   if(node[index].l == -1 && node[index].r == -1)
       index = -1;
   else if(node[index].l == -1)
       index = node[index].r;
   else if(node[index].r == -1)
       index = node[index].l;
   else
   {
       if(node[node[index].l].rand_fix < node[node[index].r].rand_fix)
       {
           l_rot(index);
           node[index].countl--;
           remove(node[index].l, nkey);
       }
       else
       {
           r_rot(index);
           node[index].countr--;
           remove(node[index].r, nkey);
       }
   }
   return true;
}
int find_value(int nkey, int index)
{
    if(node[index].countl + 1 == nkey) return node[index].key;
    else if(node[index].countl >= nkey) return find_value(nkey, node[index].l);
    else                              return find_value(nkey-node[index].countl-1,node[index].r);
}
void init()
{
   size = root = -1;
   for(int i = 1;i <= n;i++) insert(root,i);
}
int main()
{

    while(scanf("%lld %lld", &n, &m) && n != -1)
    {
        init();

        for(r = 1;r <= n;r++)
        {
            long long ii;
            long long sum = (n-r)*(n-r-1) / 2;
            if(sum >= m) ii = 1; 
            else { ii = m - sum + 1; m -= ii-1; }

            result[r] = find_value((int)ii, root);
            remove(root,result[r]);
        }
        printf("%d", result[1]);
        for(int i = 2;i <= n;i++) printf(" %d", result[i]);
        printf("\n");
    }
}


//贪心法,如果 搞出的东西一定是先由1递增,再由n递减!当然也可以仅由一部分组成
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;

int main()
{
    long long n, m;
    bool f[50001];
    int result[100001], r;
    while(scanf("%lld %lld", &n, &m) && n != -1)
    {
        r = 1; 
        memset(f,true,sizeof(f));

        for(int i = 1;i <= n;i++)
        {
            long long nxt = (n-i)*(n-i-1)/2;
            if(nxt > m)
                result[r++] = i;
            else
            {
                result[r++] = (m-nxt)+i;
                f[result[r-1]] = false;
                break;
            }
        }
        int rr = r-2;
        if(rr<1) rr = 1;
        for(int i = n;i >= rr;i--)
        {
            if(f[i]) result[r++] = i;
        }
        printf("%d", result[1]);
        for(int i = 2;i <= n;i++) printf(" %d",result[i]);
        printf("\n");
    }

}




你可能感兴趣的:(算法,insert)