zoukankan      html  css  js  c++  java
  • 题解 CF1208D 【Restore Permutation】

    (Large exttt {CF1208D})

    思路:手玩,树状数组,线段树

    这道题的思路就是先自己手玩数据,可以逐渐发现方法,思维难度不高。


    (large exttt{Meaning})

    有一个长度为 (n) ,各个数字值域为 ([1,n]) 的全排列序列 (a) ,给出序列 (b)(b_i=sum_{j=1}^{i-1}a_j imes[a_j<a_i]),请求出序列 (a)。(保证有解)


    (large exttt{Solution})

    第一种方法:

    乍一看好像毫无头绪,可以先观察下面的数据

    a={5,2,3,4,1,6}
    
    b={0,0,2,5,0,15}
    

    容易发现, (b) 序列最后一个 (0) 的位置 (a) 序列对应的位置就是 (1) ,因为没有一个数小于 (1) ,并且除 (1) 外所有数都大于 (1) (只能是 (b) 序列中最后一个 (0) )。

    找到了 (1) ,我们先将在 (b) 序列的这个位置后面的贡献减去 (1)

    就可以进一步推出 (2) 的位置,然后你会惊喜地发现找 (2) 的方法和 (1) 一样。

    注意下细节:找到最后一个 (0) 后,要将 (b) 的这一位的数赋为 (-1)

    这样用线段树维护就能搞定这道题。


    第二种方法:

    对于一开始 (a) 序列最后一个数 (x)(b) 最后一个数的值必为 ((1+x) imes x/2)

    但是对于 (x) 之前的数 (y) ,它有可能大于 (x) ,这使得 (b) 这个位置上的值不是 ((y+1) imes y/2) ,少了一个 (x)

    从后往前推,并边找边用数据结构改。

    可以发现可以用树状数组维护上述的 ((y+1) imes y/2-sum x~~~(x<y))
    。(即区间修改 (x) ,单点查询)。

    找的时候在外面套一个二分查询就行了。


    (large exttt{Code})

    第二种方法的代码(第一种没写QwQ)

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N = 2e6;
    inline int read()
    {
        register int s = 0;
        register bool neg = 0;
        register char c = getchar();
        for (; c < '0' || c > '9'; c = getchar())
            neg |= (c == '-');
        for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
            ;
        return (neg ? -s : s);
    }
    
    int a, l, r, s[N + 10], p[N + 10], ans[N + 10];
    
    inline void insert(int n, int m)
    {
        while (n <= a)
        {
            p[n] += m;
            n += n & -n;
        }
    }
    
    inline int query(int n)
    {
        int m = 0;
        while (n)
        {
            m += p[n];
            n -= n & -n;
        }
        return m;
    }
    
    signed main()
    {
        a = read();
        for (int i = 1; i <= a; i++)
            s[i] = read(), insert(i, i);
        for (int i = a; i >= 1; i--)
        {
            int l = 1, r = a, mid, sum = 0;
            while (l <= r)
            {
                mid = (l + r) >> 1;
                if (query(mid - 1) <= s[i])
                    sum = mid, l = mid + 1;
                else
                    r = mid - 1;
            }
            insert(sum, -sum);
            ans[i] = sum;
        }
        for (int i = 1; i <= a; i++)
            printf("%lld ", ans[i]);
        return 0;
    }
    
  • 相关阅读:
    Swift Optional
    cocoapods 配置
    winform窗体全屏
    SQLiteDatabase的使用
    探索Gallery和ImageSwitcher布局
    常用布局参考
    增加动画的效果
    AlertDialog的写法
    自定义Toast
    适配器的经典写法
  • 原文地址:https://www.cnblogs.com/RedreamMer/p/13620860.html
Copyright © 2011-2022 走看看