cf 1208D 树状数组+倍增/线段树

给定一个长度为n的s序列,(s_{i})表示前i-1个数中有比i小的数的总和,输出原序列p(1-n每个数出现一次)

解法一 树状数组:

倒序,对于每个(s_{i}),找出从1-n中的未被利用且和为(s_{i})的前缀和,则(p_{i})为这些数中最大的数+1,每次找到后需要及时删去

此处查找使用倍增的方法。

数组数组+倍增,单次操作O(log n)

树状数组+二分,单次操作O(log n*log n)

第三组样例过程:
5
0 1 1 1 10

1 3 3 10 5
1 3 3 10 0
1 1 3 8 0
1 1 0 5 0
1 1 0 1 0
0 0 0 0 0
输出:
1 4 3 2 5

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll s[200050], c[200050];
ll p[25];
int n, t, h[200050];
void add(int x, int t)
{
    while (x <= n)
    {
        c[x] -= t;
        x += x&-x;
    }
}
int main()
{
    p[0] = 1;
    for (int i = 1; i<22; i++)p[i] = p[i - 1] << 1;
    scanf("%d",&n);
    t = log(n) / log(2);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld",&s[i]);
        c[i] += i;
        if (i + (i&-i) <= n)c[i + (i&-i)] += c[i];
    }
    //  for(int i=1;i<=n;i++)
    //     cout<<c[i]<<" ";
    //   cout<<endl;
    for (int i = n; i; i--)
    {
        int ans = 0; ll sum = 0;
        for (int j = t; j >= 0; j--) {    
            if (ans + p[j] <= n && sum + c[ans + p[j]] <= s[i] ) {
                sum += c[ans + p[j]];
                ans += p[j];
            }
        }
        h[i] = ans+1;
        add(ans+1, ans+1);
     //   for(int i=1;i<=n;i++)
     //        cout<<c[i]<<" ";
    //      cout<<endl;
    }
    for (int i = 1; i <= n; i++) printf("%d ", h[i]);
    puts("");
    return 0;
}

解法二:线段树

#include<bits/stdc++.h>
#pragma GCC optimize(3)
#define max(a,b) a>b?a:b
using namespace std;
typedef long long ll;
const int N=2e5+5;
ll tr[N<<2];
ll s[N];
int pos[N];
void build(int l,int r,int p){
    if(l==r){
        tr[p]=l;
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,p<<1);
    build(mid+1,r,p<<1|1);
    tr[p]=tr[p<<1]+tr[p<<1|1];
}
int query(int l,int r,int p,ll val){
    if(l==r){
        tr[p]=0;
        return l;
    }
    int mid=(l+r)>>1;
    int ans=0;
    if(tr[p<<1]>val){
        ans=query(l,mid,p<<1,val);
        tr[p]-=ans;
    }
    else{
        ans=query(mid+1,r,p<<1|1,val-tr[p<<1]);
        tr[p]-=ans;
    }
    return ans;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%I64d",&s[i]);
    build(1,n,1);
    for(int i=n;i>=1;i--){
        pos[i]=query(1,n,1,s[i]);
    }
    for(int i=1;i<=n;i++) printf("%d%c",pos[i]," 
"[i==n]);
    return 0;
}

此题与POJ 2182类似。

原文地址:https://www.cnblogs.com/nuchenghao/p/11422058.html