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  阅读(155)  评论(0)    收藏  举报