D. Permutation Restoration
解法:贪心算法
分析:我们来分析第i个位置的a[i]的可能情况,发现有:
i/(b[i-1]+1)+1<=a[i]<=i/b[i]
我们有n个可能取值的区间,其中区间左右边界均小于等于n,让我们在每个区间中选取一个数,且要保证任意两个区间内选取的数不能相同,任意构造出来一组解即可。
这是一个比较经典的问题,按照贪心的思想来想,我们首先要按照n个区间的右区间从小到大进行排序,右区间越小我们越应该先给该区间分配一个数,因为我们是从小到大考虑每个数,在右区间相同的前提下我们再按照左区间从小到大排序,这也是容易理解的,
我们优先考虑 l[i] 是否能作为第i个区间所选取的数,因为在第i个区间之后的区间不一定包含l[i],而且即使包含l[i],那么这个区间的区间右边界也一定大于等于当前区间右边界,所以比当前区间的选数可能性更多,这是由于排序规则决定的,所以我们只需要从每个区间左边界开始遍历,一直到该区间右边界,当一个数尚未被选时我们就选他。
如果直接遍历是会超时的,这时候我们可以;利用STL!
set<int>rest;
for (int i = 1; i <= n; i++) rest.insert(i);
ans[p[i].id] = *rest.lower_bound(p[i].l);
rest.erase(ans[p[i].id]);用一个删一个。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mo = 1e3;
const int N = 5e5 + 5;
int a[N], b[N], ans[N], vis[N];
struct str
{
int id, l, r;
} p[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n;
cin >> n ;
set<int>rest;
for (int i = 1; i <= n; i++)
{
rest.insert(i);
cin >> b[i];
p[i].id = i;
if (b[i] != 0)
{
p[i].r = i / b[i];
p[i].l = (i / (b[i] + 1)) + 1;
}
else
{
p[i].l = i + 1;
p[i].r = n;
}
}
sort(p + 1, p + n + 1, [&](str x, str y)
{
if (x.r == y.r)return x.l < y.l;
return x.r < y.r;
});
for (int i = 1; i <= n; i++)
{
ans[p[i].id] = *rest.lower_bound(p[i].l);
rest.erase(ans[p[i].id]);
}
for (int i = 1; i <= n; i++)
{
cout << ans[i] << ' ';
}
cout << '\n';
}
return 0;
}