考場寫的亂搞,現在想到一些讓正確率更高的辦法,我猜可以過,先寫一下大體思路。
首先我們有 m 個方程 \(a_i \times x + b_i = c_i (\bmod mod)\),顯然這個模意義方程是可以加減的,我們嘗試構造一個方程:
\(x + \sum b_i = \sum c_i\),這樣如果 \(b_i\) 個數不多,就可以得到 x 的大體區間范圍。
這時候我們再構造一組方程 \(kx + \sum b_i = \sum c_i\),如果 k 不大,由剛剛求出的 x 范圍,可以發現 kx 范圍也不會很大,很大概率結果除 mod 相同,所以如果相同,我們可以得到一個新的方程 \(kx = \sum c_i - \sum b_i - p \times mod\),列出不等式 \(\sum c_i - \sum err - p \times mod \le k \times x \le \sum c_i + \sum err - p \times mod\),可以得到一個更加緊的范圍,也可以繼續更新其他的 k。
下面的問題是如何用比較少的方程構造出一個 \(kx + \sum b_i = \sum c_i\),其中 k 不能太大。
我們考慮將所有方程按 \(a_i\) 排個序,然后拿 \(a_i\) 相鄰的幾個方程相減,可以得到 \(a_i\) 更加小的一些方程,如此迭代幾次,效果應該不錯,應該迭代層數也不會太大,好像迭代一層,\(max_{a_i}\) 就會期望變成 \(\frac{max_{a_i} \log m}{m}\),而且你可以保留更多的方程,\(a_i\) 縮減會更快。
大概按照上面這么寫就有不少分了,應該可以加入一些隨機化技巧,大膽猜測是可以過掉這題的。
upt: 過了
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::cerr;
typedef long long ll;
typedef long double ld;
std::mt19937_64 rd(114514);
const int maxn = 100100;
ll mod, err;
inline ll mul(ll x, ll y) {
ll res = x * y - ll((ld) x * y / mod + 0.5) * mod;
res += res >> 63 & mod;
return res;
}
inline ll add(ll x, ll y) { return x += y - mod, x + (x >> 63 & mod); }
inline ll sub(ll x, ll y) { return x -= y, x + (x >> 63 & mod); }
int T, m;
struct fc { ll a, c, b_cnt; } A[maxn], a[maxn];
inline bool can(ll x) {
for(int i = 0;i < m;++i) {
ll val = sub(mul(A[i].a, x), A[i].c);
if(val > err && val < mod - err) return 0;
}
return 1;
}
ll L, R;
inline int operator < (const fc & x, const fc & y) { return x.a < y.a; }
inline int operator == (const fc & x, const fc & y) { return x.a == y.a; }
std::vector<fc> vector;
inline bool mul_less(ll a, ll b, ll c) {
return (ld) a * b <= c * 1.1 && a * b <= c;
}
inline void update(ll a, ll c, ll b_cnt) {
if(mul_less(b_cnt, err * 2, a == 1 ? mod : mod / 2)) {
if(mul_less(R - L + 1, a, mod)) {
ll min = mul(L, a), max = mul(R, a);
if(max < min) max += mod;
ll range = b_cnt * err, min_can = c - range, max_can = c + range;
for(;min_can > max;) min_can -= mod, max_can -= mod;
for(;max_can < min;) min_can += mod, max_can += mod;
if(min_can + mod <= max || min <= max_can - mod) return ;
if(min_can > min) L += (min_can - min) / a;
if(max_can < max) R -= (max - max_can) / a;
}
}
}
inline void emplace(ll a, ll c, ll b_cnt) {
vector.push_back((fc) { a, c, b_cnt});
update(a, c, b_cnt);
}
inline void reduce() {
vector.clear();
ll det = rd() % mod;
int need = R - L < mod / 4;
if(need) L += det, R += det;
else L = 0, R = mod - 1;
for(int i = 0;i < m;++i) {
a[i] = A[i];
a[i].c = add(a[i].c, mul(a[i].a, det));
}
for(int i = 0;i < 20000;++i) {
ll A = 0, C = 0;
for(int j = 0, p[3];j < 3;++j) {
start:
int&which = p[j] = rd() % m;
for(int k = 0;k < j;++k) if(p[j] == p[k]) goto start;
if(rd() & 1) {
A = add(A, a[which].a);
C = add(C, a[which].c);
} else {
A = sub(A, a[which].a);
C = sub(C, a[which].c);
}
}
emplace(A, C, 3);
}
for(int i = 0;i < 5;++i) {
if(vector.size() > 3000) {
nth_element(vector.begin(), vector.begin() + 3000, vector.end());
vector.resize(3000);
}
sort(vector.begin(), vector.end());
vector.erase(unique(vector.begin(), vector.end()), vector.end());
for(int size = vector.size(), i = 0;i < size;++i) {
for(int j = i + 1;j < i + 10 && j < size;++j) {
emplace(vector[j].a - vector[i].a, sub(vector[j].c, vector[i].c), vector[i].b_cnt + vector[j].b_cnt);
}
}
}
L -= det, R -= det;
}
int main() {
// freopen("1.in", "r", stdin);
std::ios::sync_with_stdio(false), cin.tie(0);
cin >> T;
for(int i = 0;i < T;++i) {
cin >> m >> mod >> err;
err = err + 1 >> 1;
for(int i = 0;i < m;++i) {
cin >> A[i].a >> A[i].c;
A[i].b_cnt = 1;
}
L = 0, R = mod - 1;
for(;R - L > 5000;) {
reduce();
// std::cerr << L << ' ' << R << '\n';
}
L = R = L + R >> 1;
for(;;-- L, ++ R) {
if(can(L)) { cout << (L + mod) % mod << std::endl; break; }
if(can(R)) { cout << (R + mod) % mod << std::endl; break; }
}
}
// std::cerr << double(clock()) / CLOCKS_PER_SEC << '\n';
}