题目概述
原题参考:B. Make Almost Equal With Mod
给出一个长度为n的数组,可以证明的是,一定存在一个整数k使得a[i]=a[i]%k之后,数组a中只有两个数,请给出整数k,当然,若有多个k,随意给出一个即可
思路想法
太巧妙了,本来以为是暴力可以过的,因为其实数组a的很小,最多100个数,当时猜的是对于任意的数,都会存在一个很小的k满足,就可以直接暴力跑过,但是事实上是错误的,还是tle了,因为ai的取值很大,最大是1e18,这里就不得不提到这个问题的解法了
就向题目说的,本题解法与二进制相关,想一下取模运算的几个例子,123%100=23,1245%1000=245...以这种十进制的取模运算很容易看出来一种截断效应,当然当k!=10^i次方时是不存在该种现象的,随便举几个二进制的例子,1011%10=1,1001111%100=11,因此二进制取模对2的幂次放也存在截断效应
,这时候我们在判断一下二进制每一位数有几种可能,每一位只有两种可能,当当前位相同时,看下一位,如果不同,那么这一位之后的都想同,这一位不同,正是两个数,否则如果这一位也相同,那么继续往下
参考代码
#include <bits/stdc++.h>
using namespace std;
#define FAST_IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define endl '\n'
#define pll pair<long long, long long>
#define pii pair<int, int>
#define vi vector<int>
#define vl vector<long long>
#define ll long long
#define ull unsigned long long
const ll INF = 9187201950435737471;
const int inf = 2139062143;
const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1.0);
const int N = 107;
ll n, a[N];
void solve() {
cin >> n;
for(int i=1; i<=n; i++) cin >> a[i];
set<ll> st;
ll k = 2;
while(true) {
int i;
for(i=1; i<=n; i++) {
st.insert(a[i]%k);
if(st.size() > 2) break;
}
if(i == n+1 && st.size() == 2) break;
else k <<= 1, st.clear();
}
cout << k << endl;
st.clear();
}
int main() {
#ifdef xrl
freopen("in.txt", "r", stdin), freopen("out.txt", "w", stdout);
#endif
FAST_IO;
int t = 1;
cin >> t;
while(t --) solve();
#ifdef xrl
cout << "Time used = " << (double)(clock() * 1.0 / CLOCKS_PER_SEC) << "s";
#endif
return 0;
}
做题反思
太巧妙了,实在是太好玩了,所以对于任意数组,其实可以通过取模运算变为一个或者两个数