思路:手玩,树状数组,线段树
这道题的思路就是先自己手玩数据,可以逐渐发现方法,思维难度不高。
(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;
}