树状数组+康托展开+二分查找 Codeforces Round #285(div.1)B (div.2)D Misha and Permutations Summation

Codeforces Round #285 (Div.1)B
Codeforces Round #285 (Div.2)D

Misha and Permutations Summation

题意:不解释,看公式挺好理解的

对于康托展开,可以看到的是,对对应的每一位,都有相同的 阶乘值,那么只要对应位相加,判断是否大于后一个阶乘值,对应处理就好。
然后求得所需排列的康托展开,用二分查找找到对应的值输出即可。

代码如下。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 2e5+10;
int tree[maxn];
int p1[maxn],p2[maxn],p3[maxn];
int n;

void add(int x,int num)
{
    while(x <=n)
    {
        tree[x]+=num;
        x+=x&-x;
    }
}

int getsum(int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=tree[x];
        x-=x&-x;
    }
    return ret;
}

int main()
{
#ifndef ONLINE_JUDGE
    //freopen("G.in","r",stdin);
    //freopen("G.out","w",stdout);
#endif
    scanf("%d",&n);
    int x;
    for(int i=1;i<=n;i++)
        add(i,1);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        p1[i]=getsum(x);
        add(x+1,-1);
    }
    memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)
        add(i,1);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        p2[i]=getsum(x);
        add(x+1,-1);
    }
    for(int i=n;i>=1;i--)
    {
        p3[i]+=p1[i]+p2[i];
        if(p3[i]>=n+1-i)
        {
            p3[i]=p3[i]-n-1+i;
            if(i!=1)
                p3[i-1]++;
        }
    }
    memset(tree,0,sizeof(tree));
    for(int i=1;i<=n;i++)
        add(i,1);
    int a,b,c;
    for(int i=1;i<=n;i++)
    {
        int l=1,r=n;
        while(l<=r)
        {
            int m=(l+r)>>1;
            a= getsum(m);
            b=getsum(m-1);
            if(b > p3[i] || (b == p3[i] && a-b==1))
                r=m-1;
            else if((b==p3[i] && a-b <1)||b<p3[i])
                l=m+1;
        }
        add(l,-1);
        printf("%d ",l-1);
    }
    printf("
");
    return 0;
}

原文地址:https://www.cnblogs.com/Combustible-ice/p/5827787.html