cf1208 D Restore Permutation (二分+树状数组)

题意

让你构造一个长度为n的序列,记为p1……pn,(这个序列是1~n的全排列的一种)

给你n个数,记为s1……sn,si的值为p1……pi-1中小于pi的数的和。

 

思路

显然,应该倒着来,也就是从p开始构造,这样的话,当要填p的时候,p1到pi-1就是所有的还未填的数,那么我们只需要去找哪个前缀和符合就可以了。

比如:现在还没填进去的数是 1,2,3,4,5

而我现在的si 是6,那么,对应的pi 就是4,因为这样无论1,2,3,5怎么排,都符合si 为6

(才学疏浅,不怎么会讲)

那么,选完要删除,所以要修改前缀和,考虑用树状数组,查找哪个符合,自然是二分。

 

代码

 

#include <stdio.h>
#include <queue>
#include <string>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <set>
using namespace std;
typedef long long int ll;
const int maxn = 2e5 + 10;
const int inf = 70000000;
const ll mod = 998244353;
const ll seed = 131;
ll s[maxn],c[maxn],ans[maxn],n;
int lowbit(int x)
{
    return x & (-x);
}
void insert(ll x,int i)
{
    while(i <= n){
        c[i] += x;
        i += lowbit(i);
    }
}
ll getsum(int i)
{
    ll sum = 0;
    while(i > 0){
        sum += c[i];
        i -= lowbit(i);
    }
    return sum;
}
int vis[maxn];
int query(ll x)
{
    int l,r,mid;
    ll res;
    l = 0;r = n;
    while(l <= r){
        mid = (l + r) >> 1;
        res = getsum(mid);
        if(res == x){
            if(vis[mid + 1])
                l = mid + 1;
            else
                return mid;
        }
        else if(res < x){
            l = mid + 1;
        }
        else{
            r = mid - 1;
        }
    }
}
int main()
{
    while(scanf("%d",&n) != EOF){
        memset(vis,0,sizeof(vis));
        memset(c,0,sizeof(c));
        for(int i = 1;i <= n;i++){
            scanf("%lld",&s[i]);
            insert(i,i);
        }

        for(int i = n;i >= 1;i--){
            ans[i] = query(s[i]) + 1;
            vis[ans[i]] = 1;
            insert(-ans[i],ans[i]);
        }
        for(int i = 1;i <= n;i++)
            printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}

 

posted @ 2020-02-18 23:55  Init_Rain  阅读(136)  评论(0编辑  收藏  举报