Loading

题解 【CF1208D Restore Permutation】

\(\Large\texttt {CF1208D}\)

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

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


\(\large\texttt{Meaning}\)

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


\(\large\texttt{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)\times x/2\)

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

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

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

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


\(\large\texttt{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;
}
posted @ 2020-09-06 10:14  RedreamMer  阅读(116)  评论(0编辑  收藏  举报