D. Permutation Restoration(贪心 + 双指针)

Educational Codeforces Round 131 (Rated for Div. 2)

Problem

有一个长度为n的a数组和b数组, 两者满足 b [ i ] = ⌊ i a [ i ] ⌋ b[i] =\lfloor \frac {i}{a[i]} \rfloor b[i]=a[i]i 的对应关系。现在给出b数组,求出任意一个满足该对应关系的a数组。

Solution

  1. 首先可以发现:对于每个b[i],有一个与之对应的一段 a[i],且a[i] 满足 ⌊ i b [ i ] + 1 ⌋ + 1 ≤ a [ i ] ≤ ⌊ i b [ i ] ⌋ \lfloor \frac {i}{b[i] + 1}\rfloor + 1 \leq a[i] \leq \lfloor \frac {i}{b[i]}\rfloor b[i]+1i+1a[i]b[i]i
  2. 然后可以贪心的对所有这些线段按照左端点从小到大排序;左端点相同时,按照右端点从小到大排序

但是,仅仅这样还不够 “贪”。

  1. 对于一个点 i ,可能会有很多段的左端点与之重合,可以选择右端点最小的一段,把i分给它。
  2. 然后对于剩余的,可以将他们与后面的左端点为i + 1的归为一类。然后把i + 1分配给其中右端点最小的一个。
  3. 对于剩余的,以此类推,归为后面的一类。

Code

#define pii pair<int, int>
const int N = 5e5 + 5, M = 1e6 + 7;
int a[N], b[N];
pair<pii, int> s[N];

int main()
{
	IOS;
	int T; cin >> T;
	while (T--)
	{
		int n; cin >> n;
		for (int i = 1; i <= n; i++)
		{
			cin >> b[i];
			//pair第一维是a[i]的范围,第二维是a[i]的下标i
			if (b[i] == 0) s[i] = { {i + 1, n}, i };
			else s[i] = { {i / (b[i] + 1) + 1, i / b[i]}, i };
		}

		sort(s + 1, s + n + 1, [](pair<pii, int> a, pair<pii, int> b) {
			if (a.ft.ft == b.ft.ft) return a.ft.sd < b.ft.sd;
			return a.ft.ft < b.ft.ft;
			});

		set<pii> se;//set堆顶是“最小值”
		int j = 1;
		for (int i = 1; i <= n; i++)
		{
			while (j <= n && s[j].ft.ft == i)
			{
				se.insert({ s[j].ft.sd, s[j].sd });
				j++;
			}
			a[se.begin()->sd] = i;
			se.erase(se.begin());
		}


		for (int i = 1; i <= n; i++)
			cout << a[i] << " ";
		cout << "\n";
		//cout << "----------\n";
	}


	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

to cling

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值