点击打开链接
点击打开链接
考虑维护使得最后一个玩家获胜的数字集合 S S S ,初始时, S = { 0 } S=\{0\} S={0} 。
考虑最后一个回合 i i i :
若该回合是 0 号玩家的回合,则有 S ′ = { x ∣ x ∈ S } ∪ { x ⊕ A i ∣ x ∈ S } S'=\{x\mid x\in S\}\cup\{x\oplus A_i\mid x\in S\} S′={x∣x∈S}∪{x⊕Ai∣x∈S} ;
若该回合是 1 号玩家的回合,则若 A i ∈ S A_i\in S Ai∈S ,则 S ′ = S S'=S S′=S ,否则, S ′ = ∅ S'=\varnothing S′=∅ 。
这是因为 S S S 是一个包含 0 0 0 的线性空间,因此若 A i ∉ S A_i\notin S Ai∈/S ,则对于所有 x ∈ S x\in S x∈S ,有 x ⊕ A i ∉ S x\oplus A_i\notin S x⊕Ai∈/S 。
那么,维护 S S S 的线性基,支持插入和询问某个元素是否在 S S S 中即可。
时间复杂度 O ( T N L o g V ) O(TNLogV) O(TNLogV) 。
#include
using namespace std;
const int MAXN = 205;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
ll a[MAXN], b[MAXN], bit[MAXN];
char s[MAXN];
void ins(ll x) {
for (int i = 60; i >= 0; i--)
if (x & bit[i]) {
if (b[i] == 0) {
b[i] = x;
break;
}
x ^= b[i];
}
}
bool query(ll x) {
for (int i = 60; i >= 0; i--)
if (x & bit[i]) x ^= b[i];
return x == 0;
}
int main() {
int T; read(T);
while (T--) {
int n; read(n);
for (int i = 1; i <= n; i++)
read(a[i]);
scanf("\n%s", s + 1);
for (int i = 0; i <= 60; i++)
b[i] = 0, bit[i] = 1ll << i;
bool win = false;
for (int i = n; i >= 1; i--) {
if (s[i] == '0') ins(a[i]);
else win |= !query(a[i]);
}
if (win) puts("1");
else puts("0");
}
return 0;
}
考虑将 0 看做 − 1 -1 −1 ,将 1 看做 + 1 +1 +1 ,求出各个位置的前缀和 S i ( 0 ≤ i ≤ N ) S_i\;(0\leq i\leq N) Si(0≤i≤N) 。
可以发现,问题要求我们最小化 S i S_i Si 集合的极差。
考虑枚举 S i S_i Si 集合的最小值 M i n Min Min ,保证 S i S_i Si 均 ≥ M i n \geq Min ≥Min 的情况 下最小化最大值 M a x Max Max 。
则应当首先将所有的 ? 替换为 1 ,若此时,仍然存在 S i < M i n S_i
否则,可以发现从前到后贪心地决定是否将一个 ? 修改为的 1 变成 0 是最优的。
则可以通过预处理 S i S_i Si 后缀最小值的方式 O ( N ) O(N) O(N) 解决该问题。
记 S i S_i Si 集合最小值的最大值为 M M M ,枚举 M i n = M , M − 1 , M − 2 , … Min=M,M-1,M-2,\dots Min=M,M−1,M−2,… 可以得到一个 O ( N 2 ) O(N^2) O(N2) 的做法。观察贪心的过程,可以发现 M i n = x − 2 Min=x-2 Min=x−2 的解不会优于 M i n = x Min=x Min=x 的解,从而可以只枚举 M i n = M , M − 1 Min=M,M-1 Min=M,M−1 ,降低时间复杂度。
时间复杂度 O ( N ) O(N) O(N) 。
#include
using namespace std;
const int MAXN = 1e6 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
char s[MAXN];
int n, a[MAXN], sMin[MAXN]; bool b[MAXN];
void init() {
for (int i = 1; i <= n; i++) {
if (s[i] == '0') a[i] = a[i - 1] - 1;
else a[i] = a[i - 1] + 1;
if (s[i] == '?') b[i] = true;
}
sMin[n + 1] = n + 5;
for (int i = n; i >= 0; i--)
sMin[i] = min(sMin[i + 1], a[i]);
}
int work(int limit) {
int mns = 0;
for (int i = 1; i <= n; i++) {
if (s[i] == '?' && sMin[i] - mns - 2 >= limit) {
b[i] = false;
mns += 2;
}
a[i] -= mns;
}
int Max = 0;
for (int i = 1; i <= n; i++)
chkmax(Max, a[i]);
return Max - limit;
}
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
int ans = n + 5, tmp = 0;
init(); chkmin(ans, work(sMin[0]));
init(); chkmin(ans, work(sMin[0] - 1));
cout << ans << endl;
return 0;
}
首先,我们显然可以将整个序列染成 1 ,因此,不妨令 A ≤ B A\leq B A≤B 。
考虑一个结果序列 S S S ,判断其是否可以到达。
对于一个长度至少为 A A A 的全 0 区间,我们可以将其任意染成一种颜色。
对于一个长度至少为 B B B 的全 1 区间,我们可以将其任意染成一种颜色。
由此,若将长度至少为 A A A 的全 0 区间全部染成 1 后,存在长度至少为 B B B 的全 1 区间,则结果序列 S S S 可以被达到。反之,不难证明,结果序列 S S S 不能被达到。
那么,记 d p i , j dp_{i,j} dpi,j 表示长度为 i i i 的区间,结尾处极长的全 1 区间的长度为 j j j ,转移时枚举下一个 1 的位置,可以得到一个 O ( N 3 ) O(N^3) O(N3) 的做法。
观察转移形式,用部分和优化即可。
时间复杂度 O ( N 2 ) O(N^2) O(N2) 。
#include
using namespace std;
const int MAXN = 5005;
const int P = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int n, a, b, dp[MAXN][MAXN], two[MAXN];
int pd[MAXN][MAXN], aux[MAXN];
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
int main() {
read(n), read(a), read(b);
if (a < b) swap(a, b); two[0] = 1;
for (int i = 1; i <= n; i++)
two[i] = 2ll * two[i - 1] % P;
dp[0][0] = 1;
for (int i = 0; i <= n; i++) {
if (i != 0) update(aux[i], aux[i - 1]);
update(dp[i][1], aux[i]);
for (int j = 1; j <= a; j++) {
update(dp[i][j], pd[i][j]);
update(pd[i + 1][min(j + 1, a)], pd[i][j]);
}
if (i == n) break;
for (int j = 0; j <= a - 1; j++) {
update(dp[i + 1][j + 1], dp[i][j]);
update(aux[i + 2], dp[i][j]);
if (i + b + 1 <= n) update(aux[i + b + 1], P - dp[i][j]);
if (i + b + 1 <= n) update(pd[i + b + 1][min(j + b + 1, a)], dp[i][j]);
if (n - i >= b) update(dp[n][min(j + n - i, a)], dp[i][j]);
}
}
int ans = 0;
for (int i = 1; i <= n; i++)
update(ans, 1ll * dp[i][a] * two[n - i] % P);
cout << ans << endl;
return 0;
}
考虑 Snuke 的最优策略,应当为:
随机操作一盏亮着的灯 x x x
若 p x = x p_x=x px=x ,则 x x x 永远不会再次亮起,游戏结束, Snuke 失败。
若 p x ≠ x p_x\ne x px=x ,则若 p x p_x px 暗了下去,则可以再次操作 x x x ,使其亮起;若 p x p_x px 亮了起来, Snuke 可以转而操作 p x p_x px ,直至将 x x x 所在的置换环全部点亮。
由于排列 p p p 的随机性,我们可以认为 Snuke 每次随机都会操作最小的尚未操作的灯。
那么,一个排列合法当且仅当其满足如下条件:
令 t t t 表示最小的,使得 p t = t , t ≤ A p_t=t,t\leq A pt=t,t≤A 的位置,若不存在,则令 t = A + 1 t=A+1 t=A+1 ,
对于所有 A + 1 ≤ x ≤ N A+1\leq x\leq N A+1≤x≤N , x x x 所在的置换环上存在一个 ≤ t − 1 \leq t-1 ≤t−1 的元素。
考虑如何计数,由于 N N N 较大,不妨考虑将置换环中 ≥ A + 1 \geq A+1 ≥A+1 的元素删去,考虑剩余的置换。
此时,在 t t t 之前,可能会有其他的满足 p x = x p_x=x px=x 的自环,但在插入 ≥ A + 1 \geq A+1 ≥A+1 的元素后,这些自环将会被补足为一个至少二元的环。在枚举 t t t 后,我们需要知道两点:
( 1 ) (1) (1) 、满足所在置换环中存在 ≤ t − 1 \leq t-1 ≤t−1 的元素的位置个数 i i i
( 2 ) (2) (2) 、满足 ≤ t − 1 \leq t-1 ≤t−1 ,且 p t = t p_t=t pt=t 的位置个数 j j j
考虑将 ≥ A + 1 \geq A+1 ≥A+1 的元素插入进置换的合法方案数,应当为
( N − A ) j ‾ × ( i + ( N − A − j ) − 1 ) N − A − j ‾ (N-A)^{\underline{j}}\times (i+(N-A-j)-1)^{\underline{N-A-j}} (N−A)j×(i+(N−A−j)−1)N−A−j
同时,对于所在置换环中不存在 ≤ t \leq t ≤t 的元素的 max { 0 , N − i − 1 } \max\{0,N-i-1\} max{0,N−i−1} 个位置,其置换的形式是任意的,即有系数
max { 0 , N − i − 1 } ! \max\{0,N-i-1\}! max{0,N−i−1}!
由此,我们可以设计动态规划,将上文的 i , j i,j i,j 计入状态。
转移时枚举最小的元素所在的置换环的大小,不难得到一个 O ( A 3 + N ) O(A^3+N) O(A3+N) 的解法。
观察转移,用部分和优化,时间复杂度降为 O ( A 2 + N ) O(A^2+N) O(A2+N) 。
#include
using namespace std;
const int MAXN = 1e7 + 5;
const int MAXM = 5e3 + 5;
const int P = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
int fac[MAXN], inv[MAXN];
int power(int x, int y) {
if (y == 0) return 1;
int tmp = power(x, y / 2);
if (y % 2 == 0) return 1ll * tmp * tmp % P;
else return 1ll * tmp * tmp % P * x % P;
}
int binom(int x, int y) {
if (y > x) return 0;
else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
void update(int &x, int y) {
x += y;
if (x >= P) x -= P;
}
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = 1ll * fac[i - 1] * i % P;
inv[n] = power(fac[n], P - 2);
for (int i = n - 1; i >= 0; i--)
inv[i] = inv[i + 1] * (i + 1ll) % P;
}
int n, m, dp[MAXM][MAXM], sum[MAXM][MAXM];
int func(int x, int y) {
return 1ll * fac[x] * inv[x - y] % P;
}
int main() {
read(n), read(m), init(n);
dp[0][0] = 1, sum[0][0] = fac[m - 1];
for (int i = 1; i <= m; i++)
for (int j = 0; j <= i; j++) {
update(dp[i][j], dp[i - 1][j - 1]);
if (i >= 2) update(dp[i][j], 1ll * sum[i - 2][j] * inv[m - i] % P);
sum[i][j] = (sum[i - 1][j] + 1ll * dp[i][j] * fac[m - i - 1]) % P;
}
int ans = 0;
for (int i = 0; i <= m; i++)
for (int j = 0; j <= i && j <= n - m; j++)
update(ans, 1ll * dp[i][j] * fac[max(m - i - 1, 0)] % P * func(n - m, j) % P * func(i + (n - m - j) - 1, n - m - j) % P);
cout << ans << endl;
return 0;
}
令 P P P 表示满足 A i ≠ B i A_i\ne B_i Ai=Bi 的球数。
考虑 C i = 1 C_i=1 Ci=1 的情况,此时,答案或是 P P P ,或是 − 1 -1 −1 。
考虑对于每个球,建边 A i → B i A_i\rightarrow B_i Ai→Bi ,由题目条件,各个点的入度均不为 0 0 0 。
定义连通块为将有向边看做无向边后,形成的连通块,则可以将连通块分为如下三类:
( 1 ) (1) (1) 、连通块中仅包含一个点,和一条指向自己的边
( 2 ) (2) (2) 、连通块中仅包含一个长度 ≥ 2 \geq 2 ≥2 的环,且不存在其它的边
( 3 ) (3) (3) 、连通块中的边数多于点数
可以发现,若存在第 ( 2 ) (2) (2) 类连通块,答案显然为 − 1 -1 −1 。
同时,若不存在第 ( 2 ) (2) (2) 类连通块,由各个点的入度均不为 0 0 0 的性质,我们可以按照拓扑序构造一组合法的方案。从而,答案为 P P P 当且仅当不存在第 ( 2 ) (2) (2) 类连通块。
考虑原有的问题,在 C i ≥ 1 C_i\geq 1 Ci≥1 的情况下,即使存在第 ( 2 ) (2) (2) 类连通块,也可能有解。
我们可以将一个连通块外的球 x x x 移入第 ( 2 ) (2) (2) 类连通块,从而处理该连通块。
那么,记第 ( 2 ) (2) (2) 类连通块的个数为 X X X ,我们必然需要额外花费 X X X 步。
考虑球 x x x 的如下几种情况:
( 1 ) (1) (1) 、 x x x 在第 ( 1 ) (1) (1) 类连通块内,此时,若要将 x x x 移出,必须将另一个球移入 x x x 所在的位置,即将 x x x 所在的连通块看做第 ( 2 ) (2) (2) 类连通块处理。这会导致 X X X 增加 1 1 1 ,同时,我们也可以处理掉 C x − 1 C_x-1 Cx−1 个连通块,总的效果是导致 X X X 减去 C x − 2 C_x-2 Cx−2 ,并且额外花费 2 2 2 步。
( 2 ) (2) (2) 、 x x x 在第 ( 2 ) (2) (2) 类连通块内,我们可以在所在连通块被处理时将 x x x 移出,处理其余连通块,并在 x x x 移回时继续处理该连通块。总的效果是导致 X X X 减去 C x − 1 C_x-1 Cx−1 ,没有额外花费步数。
( 3 ) (3) (3) 、 x x x 在第 ( 3 ) (3) (3) 类连通块内, A x = B x A_x=B_x Ax=Bx ,效果是导致 X X X 减去 C x − 1 C_x-1 Cx−1 ,并且额外花费 1 1 1 步。
( 4 ) (4) (4) 、 x x x 在第 ( 3 ) (3) (3) 类连通块内, A x ≠ B x A_x\ne B_x Ax=Bx ,效果是导致 X X X 减去 C x − 1 C_x-1 Cx−1 ,没有额外花费步数。
同时,由于第 ( 1 ) , ( 2 ) (1),(2) (1),(2) 类连通块中的元素初始时是不能动的,若 X ≠ 0 X\ne 0 X=0 ,必须存在至少一个属于情况 ( 3 ) , ( 4 ) (3),(4) (3),(4) 的球 x x x 。剩余的问题就变为了:给定若干个代价 ≤ 2 \leq 2 ≤2 的物品,求出使得物品总价值 ≥ X \geq X ≥X 的最小代价。排序后枚举代价为 1 1 1 的物品个数,用双指针计算答案即可。
时间复杂度 O ( M L o g M ) O(MLogM) O(MLogM) 。
#include
using namespace std;
const int MAXN = 1e5 + 5;
const int INF = 1e9 + 7;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
vector res[3];
bool cycle[MAXN]; int x[MAXN], y[MAXN], c[MAXN];
int n, m, ind[MAXN], oud[MAXN], f[MAXN], s[MAXN];
int find(int x) {
if (f[x] == x) return x;
else return f[x] = find(f[x]);
}
void merge(int x, int y) {
x = find(x), y = find(y);
if (x != y) {
f[y] = x;
cycle[y] = false;
s[x] += s[y];
}
}
bool cmp(int x, int y) {
return x > y;
}
int work(ll cnt, bool mode) {
sort(res[0].begin(), res[0].end(), cmp);
sort(res[1].begin(), res[1].end(), cmp);
sort(res[2].begin(), res[2].end(), cmp);
for (auto x : res[0]) cnt -= x;
int ans = INF;
if (mode == false) {
if (res[1].empty()) return INF;
cnt -= res[1][0];
res[1].erase(res[1].begin());
int cur = res[2].size(); ll sum = 0;
for (auto x : res[2]) sum += x;
while (cur >= 1 && sum - res[2][cur - 1] >= cnt)
sum -= res[2][--cur];
if (sum >= cnt) chkmin(ans, 1 + cur * 2);
for (int i = 0; i < res[1].size(); i++) {
cnt -= res[1][i];
while (cur >= 1 && sum - res[2][cur - 1] >= cnt)
sum -= res[2][--cur];
if (sum >= cnt) chkmin(ans, 1 + (i + 1) + cur * 2);
}
return ans;
} else {
if (cnt <= 0) return 0;
int cur = res[2].size(); ll sum = 0;
for (auto x : res[2]) sum += x;
while (cur >= 1 && sum - res[2][cur - 1] >= cnt)
sum -= res[2][--cur];
if (sum >= cnt) chkmin(ans, cur * 2);
for (int i = 0; i < res[1].size(); i++) {
cnt -= res[1][i];
while (cur >= 1 && sum - res[2][cur - 1] >= cnt)
sum -= res[2][--cur];
if (sum >= cnt) chkmin(ans, (i + 1) + cur * 2);
}
return ans;
}
}
int main() {
read(n), read(m);
for (int i = 1; i <= n; i++)
f[i] = i, s[i] = 1, cycle[i] = true;
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]), read(c[i]);
merge(x[i], y[i]), oud[x[i]]++, ind[y[i]]++;
}
for (int i = 1; i <= n; i++)
if (ind[i] != 1 || oud[i] != 1) cycle[find(i)] = false;
int cnt = 0, ans = 0;
for (int i = 1; i <= n; i++)
if (f[i] == i) cnt += cycle[i] && s[i] >= 2;
ll lft = cnt; bool key = false;
for (int i = 1; i <= m; i++) {
ans += x[i] != y[i];
if (c[i] >= 2) {
if (cycle[find(x[i])]) {
if (s[find(x[i])] == 1) res[2].push_back(c[i] - 2);
else res[0].push_back(c[i] - 1);
} else {
if (x[i] == y[i]) res[1].push_back(c[i] - 1);
else {
lft -= c[i] - 1;
key = true;
}
}
}
}
if (cnt == 0) {
cout << ans << endl;
return 0;
} else {
int tmp = work(lft, key);
if (tmp == INF) cout << -1 << endl;
else cout << ans + cnt + tmp << endl;
}
return 0;
}
若 G = ( A , B ) ≠ 1 G=(A,B)\ne 1 G=(A,B)=1 ,则可以令 A = A G , B = B G , C = C ( G , C ) A=\frac{A}{G},B=\frac{B}{G},C=\frac{C}{(G,C)} A=GA,B=GB,C=(G,C)C ,从而 ( A , B ) = 1 (A,B)=1 (A,B)=1 ;
若 G = ( A , C ) ≠ 1 G=(A,C)\ne 1 G=(A,C)=1 ,则可以令 A = A G , C = C G , Y = ⌊ Y G ⌋ A=\frac{A}{G},C=\frac{C}{G},Y=\lfloor\frac{Y}{G}\rfloor A=GA,C=GC,Y=⌊GY⌋ ,从而 ( A , C ) = 1 (A,C)=1 (A,C)=1
若 G = ( B , C ) ≠ 1 G=(B,C)\ne 1 G=(B,C)=1 ,则可以令 B = B G , C = C G , X = ⌊ X G ⌋ B=\frac{B}{G},C=\frac{C}{G},X=\lfloor\frac{X}{G}\rfloor B=GB,C=GC,X=⌊GX⌋ ,从而 ( B , C ) = 1 (B,C)=1 (B,C)=1 。
若 A ≥ C A\geq C A≥C 或 B ≥ C B\geq C B≥C ,则可以令 A = A % C , B = B % C A=A\%C,B=B\%C A=A%C,B=B%C 。
由此,问题转化为了 ( A , B ) = ( A , C ) = ( B , C ) = 1 (A,B)=(A,C)=(B,C)=1 (A,B)=(A,C)=(B,C)=1 的情况。
考虑方程 A x + B y ≡ 0 ( m o d C ) Ax+By\equiv 0\;(mod\;C) Ax+By≡0(modC) ,令 D = A × B − 1 D=A\times B^{-1} D=A×B−1 ,其中 B − 1 B^{-1} B−1 表示 B B B 在模 C C C 意义下的乘法逆元。则有通解:
{ x ≡ k y ≡ − k × D \left\{\begin{array}{rcl}x\equiv k\\y\equiv -k\times D\end{array} \right. {x≡ky≡−k×D
注意到若存在两组解 ( x 0 , y 0 ) , ( x 1 , y 1 ) (x_0,y_0),(x_1,y_1) (x0,y0),(x1,y1) 满足 x 0 ≤ x 1 , y 0 ≤ y 1 x_0\leq x_1,y_0\leq y_1 x0≤x1,y0≤y1 ,则 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 是不优的,我们不会使用。考虑求出在这个意义下,我们可能使用的那些解。
考虑如下子问题:给定 C × D C\times D C×D 的平面,从原点处出发,一束光线沿第一象限角平分线方向发射,达到 x = C x=C x=C 时,令 x = 0 x=0 x=0 ,达到 y = D y=D y=D 时,令 y = 0 y=0 y=0 。我们希望求出 y = D y=D y=D 上被光束击中时是前缀最大值的点。
考虑一个 D × D D\times D D×D 的正方形,可以发现,光束从任意点射入,都将从其对面的点射出,从而可以删去一个极大的,包含原点的正方形。利用类似欧几里得算法的过程重复删去正方形,我们可以得到所求的所有可能使用的解。它们在平面上形成了 O ( L o g V ) O(LogV) O(LogV) 段线段。
进一步思考上述算法的过程,我们还可以发现,这些解构成了一个下凸壳。
我们现在想要选择若干个向量,满足求和后,两维均小于等于 ( X , Y ) (X,Y) (X,Y) 。
由解集的凸性,我们一定只会使用将方向 ( X , Y ) (X,Y) (X,Y) 夹在中间的两种向量。
由此,二分答案,找到这两种向量,判断答案是否合法即可。
时间复杂度 O ( T L o g V ) O(TLogV) O(TLogV) 或 O ( T L o g 2 V ) O(TLog^2V) O(TLog2V) 。
以下代码实现的是 O ( T L o g 2 V ) O(TLog^2V) O(TLog2V) 的解法。
#include
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
template void chkmax(T &x, T y) {x = max(x, y); }
template void chkmin(T &x, T y) {x = min(x, y); }
template void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
void exgcd(int a, int b, int &x, int &y) {
if (b == 0) {
x = 1, y = 0;
return;
}
int q = a / b, r = a % b;
exgcd(b, r, y, x);
y -= q * x;
}
int inv(int x, int p) {
int a = 0, b = 0;
exgcd(x, p, a, b);
return (a % p + p) % p;
}
struct info {int x, y, c; } q[MAXN];
int top, a, b, c, x, y;
void work(int c, int d){
q[top = 0] = (info) {0, 0, 0};
int x, a = 1, b = 0;
while (d != 0) {
info tmp = q[top];
q[++top] = (info) {tmp.x + c / d * a, tmp.y + c / d * d, c / d};
for (int t = 1; t <= 2 && d != 0; t++){
if (t == 1) b += c / d * a;
else a += c / d * b;
x = c % d, c = d, d = x;
}
}
}
ll func(ll x, ll y) {
if (x < 0) return -1;
else return x / y;
}
int main() {
int T; read(T);
while (T--) {
read(a), read(x), read(b), read(y), read(c);
int g = __gcd(a, b); a /= g, b /= g, c /= __gcd(c, g);
g = __gcd(a, c), a /= g, c /= g, y /= g;
g = __gcd(b, c), b /= g, c /= g, x /= g;
a %= c, b %= c;
if (c == 1) {
printf("%d\n", x + y);
continue;
}
int d = 1ll * a * inv(b, c) % c; work(c, d);
if (q[top].x != c) q[++top] = (info) {c, c, 1};
int ans = 0;
for (int i = 1; i <= top; i++) {
int lx = q[i - 1].x, rx = q[i].x;
int ly = c - q[i - 1].y, ry = c - q[i].y;
int dx = (rx - lx) / q[i].c, dy = (ly - ry) / q[i].c;
int l = ans + 1, r = x + y;
while (l <= r) {
int mid = (0ll + l + r) / 2;
ll s = func(x - 1ll * mid * lx, dx);
ll t = func(y - 1ll * mid * ry, dy);
if (s >= 0 && t >= 0 && s + t >= 1ll * q[i].c * mid) ans = mid, l = mid + 1;
else r = mid - 1;
}
}
printf("%d\n", ans);
}
return 0;
}