您的位置:首页 > 其它

【NOIP2017提高A组冲刺11.4】Pacifist

2017-11-06 19:11 309 查看

Description:

在你面前有一个被加密了的数组,其原数组是一个等差序列,你面前的则是将原数组中的所有数字都对m 取模再打乱后而得到的新数组

papyrus 给你出的谜题就是还原出原等差序列

保证数据有解,而且因为papyrus 喜欢质数,所以他给你出的谜题中的m 一定是质数

2 <= m <= 10^9 +7; 2 <= n <= 10^5,m 是质数,数组a 中所有数字两两不同

题解:

考场时瞎搞了个随机化,结果差点过了,事实证明这是对的,只是没有特判一种情况。

目标在于求出公差,求出来了一切都好办。

考虑对于原等差数列,我选取一个数x,则会呈现这样的情况:

x - kb, …, x- b, x, x+b, …,x + kb, x + (k + 1)b,…

或者是:

…,x - (k + 1)b, x - kb, …, x - b, x, x +b,…,x+kb

发现x两边的数是关于它对称的。

于是可以推出一个结论,如果有a+b = 2x, 则a和b在原序列中的位置关于x对称,前提是n != mo。

于是可以随机一个x,如果a+b=2x,那么a,b这些数和x是原序列中连续的一段,剩余的也是原序列中连续的一段。

可以选取小的一段进行递归,直到剩两个数的时候就可以求出公差了。

一定要特判n = mo,不然会死循环。

时间复杂度可以认为是O(n log n)的。

出题人的做法是枚举首项,可以根据等差数列一次和公式推出公差,再代入二、三次和公式即可判断这个首项是否合法。

Code:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
#define fo(i, x, y) for(ll i = x; i <= y; i ++)
using namespace std;

const int N = 2e5 + 5;
const ll ni_2 = 5e8 + 4;

ll mo, n, a
, b
;

ll rand(int x, int y) {
ll s = 0; fo(i, 1, 5) s = (s + rand()) % (y - x + 1);
return s + x;
}

int d1
, d2
;

ll P(ll i, ll j) {
if(j == i + 1) {
int k = (a[j] - a[i] + mo) % mo;
return k > mo / 2 ? mo - k : k;
}
ll m = rand(i, j), r = j;
ll t1 = 0, t2 = 0;
fo(k, i, j) {
int ans = 0; ll x = (2 * a[m] - a[k] + mo) % mo;
for(int l = i, r = j; l <= r; ) {
int m = l + r >> 1;
if(a[m] == x) {
ans = m; break;
}
if(a[m] < x) l = m + 1; else r = m - 1;
}
if((ans && ans != k) || k == m) d1[++ t1] = a[k]; else d2[++ t2] = a[k];
}
fo(k, i, i + t1 - 1) a[k] = d1[k - i + 1];
fo(k, i + t1, j) a[k] = d2[k - (i + t1) + 1];
if((t1 < t2 && t1 != 1) || t2 == 0 || t2 == 1) return P(i, i + t1 - 1); else return P(i + t1, j);
}

int main() {
freopen("pacifist.in", "r", stdin);
freopen("pacifist.out", "w", stdout);
srand(51651131);
scanf("%lld %lld", &mo, &n);
fo(i, 1, n) scanf("%lld", &a[i]);
if(n == mo) {
printf("0 1\n");
return 0;
}
sort(a + 1, a + n + 1);
ll g = P(1, n);
sort(a + 1, a + n + 1);
int r = 1;
fo(i, 1, n) {
int x = (a[i] - g + mo) % mo;
int ans = 0;
for(int l = 1, r = n; l <= r; ) {
int m = l + r >> 1;
if(a[m] == x) {
ans = 1;
break;
}
if(a[m] < x) l = m + 1; else r = m - 1;
}
if(!ans) {
printf("%lld %lld\n", a[i], g);
return 0;
}
}
printf("%lld %lld\n", a[1], g);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: