题目:http://codeforces.com/contest/1157/problem/E
题意:给定整数型n,a与b数组长度均为n,其中的a与b中的每个元素大小为0<= a[i] (或者b[i]) <n,现在可以改变b数组的元素顺序,用现在的b数组与a数组做如下操作:
c[i] = ( a[i] + b[i] ) % n
最后操作完之后的c数组要保证字典序最小。输出这样的c数组
思路:因为是模操作并且元素的数据范围容易让我们想到这里面的模操作会形成闭环。比如n=5,当前a=2,那么b=0,1,2,3,4对应的结果为2,3,4,0,1形成了闭环。并且这样的环不会重叠,毕竟a与b的数据范围在这里放着。这里n-a=3是最优的答案,那么如果找不到这样的刚刚好的答案怎么选择?
lower_bound(n-a)找到是距离现在n-a最近的点,但是会有一个疑问在n-a有左右两种选择,我们究竟是向左选择还是向右选择?显然lower_bound(n-a)是向右贪心选择的,怎么才能排除左边的点呢?
这里有两种情况,如果b数组中没有一个数大于等于n-a,那么是不是理解为此时的lower_bound找到的是end()?此时最优的就是左数第一个元素最优咯。第二种情况,lower_bound能够找到,设当前找到的元素为find,那么我们就是比较(find+a)%n与(a+b[0])%n的大小,因为find+a>n但是一定是小于2n的,所以(find+a)%n = find+a-n,发现什么了吗?find+a-n<a<=a+b[0],所以此时的向右找到的第一个值一定是对的。
AC code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
int n;
int a[maxn],b[maxn];
multiset<int>res;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
res.insert(b[i]);
}
for(int i=1;i<=n;i++)
{
int tmp=n-a[i];
auto findd=res.lower_bound(tmp);
if(findd==res.end())
findd=res.begin();
int ans=*findd;
res.erase(findd);
printf("%d%c",(ans+a[i])%n,i==n?'\n':' ');
}
return 0;
}