Codeforces Round #285 (Div. 1) problem B. Misha and Permutations Summation 康拓展开。

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"><span style="white-space:pre">	</span>题目地址 http://codeforces.com/contest/504/problem/B      </span>

这道题目让我更深入的了解了康拓展开。以前只是在做8数码的时候接触了康拓展开了。在比赛的时候想到了康拓展开,但是不知道怎么逆展开,所以没做出来。先介绍一下康拓展开。

康拓展开:

把一个排列展开成如下形式:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[2]*1!+a[1]*0! [1]  
其中,a为整数,并且0<=a[i]<i(1<=i<=n)  。其实就是表示的比这个排列要小的排列有多少个。 
 正好是题目的要求。但是由于数据很大,我们不能用一个数来表示,所以我们先求出康拓展开的数组q[i]和p[i],然后相加的R[i]。重点在后面,怎么取模。这里有一个很重要的结论 .a[i]<i-1。至于为什么,自己想想。那么我们可以想高精度那样向前取模了。取模完成以后就是一个逆展开。逆展开其实就是找到没用用过的数字里面的第R[i]+1的数。可以用2分+树状数组  或者直接平衡树来解决。
解法一 :康拓展开+2分+树状数组
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mod=99999997;
const int mmax=200010;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3fffffff;
#define debug
#define mmax 200010
typedef __int64 LL;
int n;
int p[mmax],q[mmax],R[mmax],c[mmax];
bool use[mmax];
int low_bit(int x)
{
    return x&(-x);
}
void updata(int pos)
{
    for(int i=pos;i<=n;i+=low_bit(i))
        c[i]++;
}
int get_sum(int pos)
{
    int ans=0;
    for(int i=pos;i>0;i-=low_bit(i))
        ans+=c[i];
    return ans;
}
int main()
{
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&p[i]),p[i]++;
        for(int i=1;i<=n;i++)
            scanf("%d",&q[i]),q[i]++;
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            updata(q[i]);
            q[i]=q[i]-1-get_sum(q[i]-1);
        }
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            updata(p[i]);
            p[i]=p[i]-1-get_sum(p[i]-1);
        }
        for(int i=1;i<=n;i++)
            R[i]=q[i]+p[i];
        for(int i=n;i>=1;i--)
        {
            R[i-1]+=R[i]/(n-i+1);
            R[i]%=(n-i+1);
        }
        memset(c,0,sizeof c);
        memset(use,0,sizeof use);
        for(int i=1;i<=n;i++)
        {
            int l=R[i]+1,r=n,mid;
            while(l<r)
            {
                mid=(l+r)>>1;
                if(mid-1-get_sum(mid-1)-R[i]==0&&(!use[mid]))
                    break;
                if(mid-1-get_sum(mid-1)-R[i]>0)
                    r=mid;
                else
                    l=mid+1;
            }
            mid=(l+r)>>1;
            R[i]=mid;
            use[R[i]]=1;
            updata(R[i]);
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d%c",R[i]-1,i==n?'\n':' ');
        }

    }
    return 0;
}
解法2   康拓展开+平衡树 
 
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<string>
#include<cstring>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#include<stdlib.h>
using namespace std;
const int mod=99999997;
const int mmax=200010;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3fffffff;
#define debug
#define mmax 200010
typedef __int64 LL;
int n;
int p[mmax],q[mmax],R[mmax],c[mmax];
bool use[mmax];
int low_bit(int x)
{
    return x&(-x);
}
void updata(int pos)
{
    for(int i=pos;i<=n;i+=low_bit(i))
        c[i]++;
}
int get_sum(int pos)
{
    int ans=0;
    for(int i=pos;i>0;i-=low_bit(i))
        ans+=c[i];
    return ans;
}



struct Treap
{
    int key,s,r;
    Treap * ch[2];
    Treap(int key0)
    {
        key=key0;
        s=1;
        r=rand();
        ch[0]=ch[1]=0;
    }
    void up()
    {
        s=1;
        s+=(ch[0]?ch[0]->s:0);
        s+=(ch[1]?ch[1]->s:0);
    }

};
typedef Treap * S_Treap ;
S_Treap root;
void T_clear(S_Treap &p)
{
    if(!p)
        return ;
    T_clear(p->ch[0]);
    T_clear(p->ch[1]);
    delete p,p=0;
}
void T_rotate(S_Treap &fa,int d)//d=0左旋 ,d=1右旋
{
    S_Treap son=fa->ch[d^1];
    fa->ch[d^1]=son->ch[d];
    son->ch[d]=fa;
    fa->up();
    son->up();
    fa=son;
}
void T_insert(S_Treap &p,int key)
{
    if(!p)
    {
        p=new Treap(key);
        return ;
    }
    if(p->key==key)
        return ;
    int d=p->key<key;
    T_insert(p->ch[d],key);
    if(p->r<p->ch[d]->r)
        T_rotate(p,d^1);
    p->up();
}

int T_kth(int k,S_Treap root)
{
    if(!root||!(k>=1&&k<=root->s))
        return -1;
    S_Treap p=root;
    while(1)
    {
        int num=p->ch[0]?p->ch[0]->s:0;
        if(num+1==k)
            return p->key;
        else if(num+1>k)
            p=p->ch[0];
        else
        {
            p=p->ch[1];
            k-=num+1;
        }
    }
}
void f_delete(S_Treap &p,int &key)
{
    if(key == p->key)
    {
        if(p->ch[0] && p->ch[1])
        {
            bool d = (p->ch[0]->r > p->ch[1]->r);
            T_rotate(p, d);
            f_delete(p->ch[d], key);
        }
        else
        {
            S_Treap del=p;
            p = p->ch[p->ch[0] ? 0:1];
            delete del,del=0;
        }
    }
    else
        f_delete(p->ch[p->key < key], key);
    if(p)
        p->up();
}
int main()
{
    while(cin>>n)
    {
        for(int i=1;i<=n;i++)
            scanf("%d",&p[i]),p[i]++;
        for(int i=1;i<=n;i++)
            scanf("%d",&q[i]),q[i]++;
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            updata(q[i]);
            q[i]=q[i]-1-get_sum(q[i]-1);
        }
        memset(c,0,sizeof c);
        for(int i=1;i<=n;i++)
        {
            updata(p[i]);
            p[i]=p[i]-1-get_sum(p[i]-1);
        }
        for(int i=1;i<=n;i++)
            R[i]=q[i]+p[i];
        for(int i=n;i>=1;i--)
        {
            R[i-1]+=R[i]/(n-i+1);
            R[i]%=(n-i+1);
        }
        T_clear(root);
        for(int i=1;i<=n;i++)
            T_insert(root,i);
        for(int i=1;i<=n;i++)
        {
            R[i]=T_kth(R[i]+1,root);
            f_delete(root,R[i]);
        }
        for(int i=1;i<=n;i++)
        {
            printf("%d%c",R[i]-1,i==n?'\n':' ');
        }

    }
    return 0;
}



你可能感兴趣的:(ACM,康拓展开)