zoukankan      html  css  js  c++  java
  • 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类似。

  • 相关阅读:
    office(PPT、Word、Excel)功能目录
    《数商》笔记
    笔记模板
    如何运用思维导图安排考生读书计划(增订版)
    自制桌面图片
    selenium+AutoIt
    selenium中应用问题解决
    mysql卸载
    vue每次修改刷新当前子组件
    vue与dajngo
  • 原文地址:https://www.cnblogs.com/nuchenghao/p/11422058.html
Copyright © 2011-2022 走看看