杂题选记(10.26 - 11.1)
P12029 [USACO25OPEN] Election Queries G
记 \(c_i\) 为投票给 \(i\) 的人数。那么两头奶牛 \(x\),\(y\) 能成为领队的条件是 \(c_x + c_y \ge c_max\),其中 \(c_max = \max{c_i}\)。
考虑单次查询,用双指针可以轻松地做到 \(O(n)\),二分带个 \(\log\) 也可以接受的。
卡在了一个重要结论,由于 \(\sum{c_i} = n\),那么不同的 \(c_i\) 的取值最多有 \(O(\sqrt{n})\) 种(\(1 + 2 + \cdots + n = \frac{n(n + 1)}{2}\))。
那么我们开 \(N\) 个 \(set\) 记录 \(c_x = i\) 中最小/最大的 \(x\),然后每次对还存在的 \(c_i\) 做一遍尺取就好了。
复杂度 \(O(Q{\log{N} + \sqrt{N}})\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
set<int> s, mn[N];
set<int, greater<int> > mx[N];
int c[N], a[N], n, q;
void upd(int x, int k){
if(c[x]){
mx[c[x]].erase(x), mn[c[x]].erase(x);
if(mx[c[x]].empty()) s.erase(c[x]);
}
c[x] += k;
if(c[x]){
mx[c[x]].insert(x), mn[c[x]].insert(x);
if(mx[c[x]].size() == 1) s.insert(c[x]);
}
}
int qry(){
auto l = s.begin(), r = s.end();
int mny = 1e9, cmax = *s.rbegin(), ans = 0;
// cout << cmax << '\n';
while(l != s.end()){
while(r != s.begin() && *prev(r) + *l >= cmax) --r, mny = min(mny, *mn[*r].begin());
ans = max(ans, *mx[*l].begin() - mny);
++l;
}
return ans;
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
cin >> n >> q;
for(int i = 1; i <= n; ++i) cin >> a[i], c[a[i]]++;
for(int i = 1; i <= n; ++i){
if(c[i]) s.insert(c[i]), mx[c[i]].insert(i), mn[c[i]].insert(i);
}
while(q--){
int x, k;
cin >> x >> k;
upd(a[x], -1);
a[x] = k;
upd(a[x], 1);
cout << qry() << '\n';
}
return 0;
}
P12030 [USACO25OPEN] OohMoo Milk G
怎么说呢,感觉这题很玄乎。
可以发现,Fj 每天的操作就是对最大的 \(A\) 个数 + 1,然后 Nj 的操作就是对最大的 \(B\) 个数 - 1。
我们发现 Fj 每天加的那 \(A\) 个数总是固定的,就是原序列中最大的 \(A\) 个数。那么现在难点在于 Nj 的操作。
先考虑对前 \(A\) 大的数全部加 \(D\)。对于一些特别大的数,Nj 每次操作都会选择减他们,相当于这些数占用了一些固定的位置。但对于一些更小的数来说,我们要最小化 \(\sum x^{2}\),根据凸性/均值,让他们更加平均是更优的。那么就类似云浅那场的 T2。我们考虑二分这个平均值 \(k\),对于 \(a_i > k + D\) 的,这就是那些每次都必须减的数,会耗费 \(D\) 个操作次数。而对于其他的 \(a_i > k\),我们会耗费 \(a_i - k\) 的操作次数把他们全部都调成这个平均值 \(k\)。由于题目限制总共操作次数不得超过 \(B \times D\)。我们找到最大的 \(k\) 使得操作次数刚好 \(\ge B \times D\)。假设 \(k\) 时耗费的次数是 \(check(k)\),根据上面调整的过程,不难发现我们二分出的这个 \(k\),它的 \(check(k) - B \times D < B\)。之所以会有这些超的,是因为整数均值就是一些是 \(k\),一些是 \(k + 1\)。那么我们把多的那部分再调成 \(k + 1\) 即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, mod = 1e9 + 7;
int n, d, a, b, m[N];
int check(int k){
int cnt = 0;
for(int i = 1; i <= a; ++i){
if(m[i] > k) cnt += min(d, m[i] - k);
}
return cnt;
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(0);
cin >> n >> d >> a >> b;
for(int i = 1; i <= n; ++i) cin >> m[i];
sort(m + 1, m + 1 + n, greater<int>());
for(int i = 1; i <= a; ++i) m[i] += d;
int l = 1, r = 2e9;
while(l < r){
int mid = (l + r + 1) >> 1;
if(check(mid) < b * d) r = mid - 1;
else l = mid;
}
int k = check(l) - b * d, ans = 0;
for(int i = 1; i <= n; ++i){
if(m[i] > l) m[i] -= min(d, m[i] - l);
(ans += m[i] * m[i]) %= mod;
}
(ans += k * (2 * l + 1)) %= mod;
cout << ans;
return 0;
}
CF2150B Grid Counting
卡在了怎么处理 \(\max(x_i, y_i) = k\) 的条件上。事实上,可以把每个点的 \(\max(x_i, y_i)\) 画出来,发现是从左上角一圈一圈递增;同理,\(\max(x_i, n - y_i + 1)\) 是从右上角一圈一圈递增。稍微找一下规律,可以发现最终能涂色的那一部分是一个倒三角状物,那么再根据 \(a_i\) 的限制从下往上填算方案就好。
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5, mod = 998244353;
ll a[N], n, k, fac[N], ifac[N], inv[N];
ll C(int n, int m){
if(m > n || n < 0) return 0;
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
void solve(){
cin >> n;
k = 0;
for(int i = 1; i <= n; ++i) cin >> a[i], k += a[i];
if(k != n || a[1] < 2) cout << 0 << '\n';
else{
ll ans = 1, cnt = 0;
for(int i = (n + 1) / 2; i >= 1; --i){
ans = (ans * C(n - 2 * (i - 1) - cnt, a[i])) % mod;
cnt += a[i];
}
for(int i = (n + 1) / 2 + 1; i <= n; ++i){
if(a[i] != 0){ ans = 0; break; }
}
cout << ans << '\n';
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
fac[0] = fac[1] = ifac[0] = ifac[1] = inv[1] = 1;
for(int i = 2; i <= 2e5; ++i){
fac[i] = (fac[i - 1] * i) % mod;
inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
ifac[i] = (ifac[i - 1] * inv[i]) % mod;
}
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2150C Limited Edition Shop
推等价条件:记 \(i\) 在 \(b_i\) 中出现的位置是 \(pos_i\),那么 Alice 能从她喜爱的物品中选出一个子集 \(S\) 的条件是 \(\forall a_i \notin S, a_j \in S, i < j\) 要满足 \(pos_{a_i} < pos_{a_j}\)。
考虑 dp,\(f_{i, j}\) 表示 Alice 选到前 \(i\) 个物品,之前没选的物品中最大的 \(pos_{a_k}(k \le i) = j\) 的最大喜爱值。
有 \(\begin{cases}
f_{i + 1, j} \gets f_{i, j} + v_{a_i + 1} & j < pos_{a_i + 1} \\
f_{i + 1, j} \gets \max_{k < j}{f_{i, k}} & j = pos_{a_i + 1} \\
f_{i + 1, j} \gets f_{i, j} & j > pos_{a_i + 1} \\
\end{cases}\)。
考虑线段树直接维护 \(f_j\)。从 \(i \to i + 1\) 时,发现 \(f_j\) 的转移是一段区间加,一次区间查最大值,和一次单点修改。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 5, inf = 1e18;
int n, v[N], a[N], b[N], pos[N], tag[N << 2], tr[N << 2];
#define ls(p) p << 1
#define rs(p) p << 1 | 1
void pushup(int p){ tr[p] = max(tr[ls(p)], tr[rs(p)]); }
void addtag(int p, int k){ tag[p] += k, tr[p] += k; }
void pushdown(int p){ if(tag[p]) addtag(ls(p), tag[p]), addtag(rs(p), tag[p]), tag[p] = 0; }
void add(int L, int R, int k, int p = 1, int pl = 0, int pr = n){
if(L <= pl && R >= pr) return addtag(p, k);
int mid = (pl + pr) >> 1;
pushdown(p);
if(L <= mid) add(L, R, k, ls(p), pl, mid);
if(R > mid) add(L, R, k, rs(p), mid + 1, pr);
pushup(p);
}
void upd(int x, int k, int p = 1, int pl = 0, int pr = n){
if(pl == pr) return tr[p] = k, void();
int mid = (pl + pr) >> 1;
pushdown(p);
if(x <= mid) upd(x, k, ls(p), pl, mid);
else upd(x, k, rs(p), mid + 1, pr);
pushup(p);
}
int query(int L, int R, int p = 1, int pl = 0, int pr = n){
if(L <= pl && R >= pr) return tr[p];
int mid = (pl + pr) >> 1, ret = -inf;
pushdown(p);
if(L <= mid) ret = query(L, R, ls(p), pl, mid);
if(R > mid) ret = max(ret, query(L, R, rs(p), mid + 1, pr));
return ret;
}
void clear(int p = 1, int pl = 0, int pr = n){
tag[p] = tr[p] = 0;
if(pl == pr) return;
int mid = (pl + pr) >> 1;
clear(ls(p), pl, mid), clear(rs(p), mid + 1, pr);
}
void solve(){
cin >> n;
clear();
for(int i = 1; i <= n; ++i) cin >> v[i];
for(int i = 1; i <= n; ++i) cin >> a[i];
for(int i = 1; i <= n; ++i) cin >> b[i], pos[b[i]] = i;
upd(0, v[a[1]]);
if(1 < pos[a[1]]) add(1, pos[a[1]] - 1, -inf);
if(pos[a[1]] < n) add(pos[a[1]] + 1, n, -inf);
for(int i = 2; i <= n; ++i){
int mx = query(0, pos[a[i]]);
add(0, pos[a[i]] - 1, v[a[i]]); // chosen
upd(pos[a[i]], mx); // unchosen
}
cout << tr[1] << '\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2150D Attraction Theory
还得练推条件。
\(n\) 个人的相对位置是不会变的。假设最终在 \(i\) 上的人有 \(f_i\) 个,可以发现 \(f\) 与 \(p\) 序列一一对应。
首先 \(f\) 有值位置是一段区间 \([l, r]\),并且 \(\sum_{x = l}^{r} f_x = n\)。就完了吗?别推着推着掉了性质,由于向中间聚拢的话,两边的数都会加到现在的位置,那么还要求 \(f_x(l < x < r)\) 是奇数,对两个边界上的数没有要求。归纳法可证。
一种 \(f\) 序列的贡献是 \(\sum_{x = l}^r f_x \times a_x\)。考虑对相同长度的部分一起计数。假设长度是 \(k\),\(Ans = \sum {\sum_{i = 1}^k f_i \times a_i} = \sum_{i = 1}^k a_i {\sum{f_i}}\)。那么我们只需要求所有方案 \(f\) 在某一位置上的和即可。中间有奇性限制,两边却没有。那么设 \(f_{1} = 2 \times g_1 + x, f_{n} = 2 \times g_n + y(x, y \in \{1,2\})\),中间的 \(f_i = 2 \times g_i + 1\)。那么限制就变成了 \(2\times (\sum_{i = 1}^k g_i) + k + (x - 1) + (y - 1)= n\)。为避免分讨,直接枚举 \(x, y\)。令 \(S = n - k - (x - 1) - (y - 1)\),如果 \(S\) 为奇数肯定无解,如果为偶数,那么 \(\sum_{i = 1}^k g_i = \frac{S}{2}\),方案数直接插板法即可,为 \(ways = {{\frac{S}{2} + k - 1} \choose {k - 1}}\)。发现每个位置上的 \(\sum{f_i}\) 形如 \(2\sum{g_i} + ways \times C\),\(C\) 是一些常数,那么对这些常数单独拿出来做。然后发现 \(g_i\) 是完全对称的,每个位置上所有方案的和都相等。而总和是 \(\frac{S}{2} \times ways\)。那么每个位置上就是 \({S \times ways} \over {2k}\)。
然后再把所有长度为 \(k\) 的区间的 \(a_i\) 之和算出来就好,这个简单计数即可。注意特判 \(l = r\)。
关注对称性是一个很棒的 trick(也可以从期望的角度理解)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, mod = 998244353;
typedef long long ll;
ll f[N], g[N], fac[N], ifac[N], inv[N], n, a[N];
ll C(int n, int m){
if(m > n) return 0;
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
}
void solve(){
cin >> n;
ll ans = 0;
for(int i = 1; i <= n; ++i){
cin >> a[i];
ans = (ans + n * a[i]) % mod;
f[i] = (f[i - 1] + a[i]) % mod;
g[i] = (g[i - 1] + 1ll * i * a[i] % mod) % mod;
}
auto get = [](int k){
int l = n - k + 1, r = k, x = min(l, r), y = max(l, r); ll ans = 0;
ans = (ans + g[x]) % mod;
if(l < r) ans = (ans + (n - k + 1) * (f[r] - f[l]) % mod) % mod;
if(r < l) ans = (ans + k * (f[l] - f[r]) % mod) % mod;
ans = (ans + (n + 1) * (f[n] - f[y]) % mod - g[n] + g[y]) % mod;
return ans;
};
for(int k = 2; k <= n; ++k){
ll sm = get(k);
for(int x : {1, 2}){
for(int y : {1, 2}){
int s = n - x - y - k + 2;
if(s & 1) continue;
s /= 2;
ans = (ans + C(s + k - 1, k - 1) * ((2ll * s * inv[k] % mod + 1) % mod * sm % mod +
1ll * (x - 1) * f[n - k + 1] % mod + 1ll * (y - 1) * (f[n] - f[k - 1]) % mod) % mod) % mod;
}
}
}
cout << (ans % mod + mod) % mod << '\n';
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
fac[1] = ifac[1] = inv[1] = fac[0] = ifac[0] = 1;
for(int i = 2; i <= 2e5; ++i){
fac[i] = fac[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
ifac[i] = ifac[i - 1] * inv[i] % mod;
}
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2138A Cake Assignment
犯唐了的一道题。因为任意时刻肯定是一个人 \(>2^k\),另一个人 \(<2^k\),那么我们倒过来做。每次那个 \(>2^k\) 的那个人肯定是上一步另一个人给了一半给它(因为如果它自己给了一半肯定不行)。
正着做也可以,就是我太蠢了手模了好久好久。下面是正着做的。
code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x, k;
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--){
cin >> k >> x;
ll D = (1ll << k);
char op[2] = {'1', '2'};
if(x == D){ cout << "0\n\n"; continue; }
if(x < D) swap(op[0], op[1]), x = (D << 1) - x;
int l;
for(l = 0; l <= k; ++l){
if(x & (1ll << l)) break;
}
cout << k - l << '\n';
for(int i = l + 1; i <= k; ++i){
cout << op[bool(x & (1ll << i))] << ' ';
}
cout << '\n';
}
return 0;
}
CF2138B Antiamuny Wants to Learn Swap
只用操作 1 的答案等于区间逆序对数。
考虑什么时候使用操作二能减少次数,对相邻三个的大小关系做讨论,发现只有当 \([l, r]\) 的这个区间中存在 \(b_i > b_{j} > b_{k}(i < j < k)\) 才可能变少。这里不是相邻的因为我们总是可以把这三个数先一操作换到一起,然后再做二操作。
那么对于每个 \(i\),找到前面的一个比它大的 \(pre_i\),后面第一个比它小的 \(nxt_i\)。然后对每个区间数 \(l \le pre_i < nxt_i \le r\) 的点有几个就行。
Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
const int N = 5e5 + 5;
int a[N], pre[N], nxt[N], n, st[N], tp, q, ans[N];
vector<pii> query[N];
struct BIT{
int tr[N];
#define lb(x) (x & (-x))
void add(int x, int k){
if(x == 0) return;
for(int i = x; i <= n; i += lb(i)) tr[i] += k;
}
int qry(int x){
int sum = 0;
for(int i = x; i; i -= lb(i)) sum += tr[i];
return sum;
}
}tr;
void solve(){
cin >> n >> q;
for(int i = 1; i <= n; ++i) cin >> a[i];
tp = 0;
for(int i = 1; i <= n; ++i){
tr.tr[i] = 0;
query[i].clear();
while(tp && a[st[tp]] > a[i]) nxt[st[tp]] = i, --tp;
st[++tp] = i;
}
for(int i = 1; i <= tp; ++i) nxt[st[i]] = n + 1;
tp = 0;
for(int i = 1; i <= n; ++i){
while(tp && a[st[tp]] < a[i]) --tp;
pre[i] = st[tp];
st[++tp] = i;
}
for(int i = 1; i <= n; ++i) query[nxt[i]].emplace_back(pre[i], 0);
for(int i = 1; i <= q; ++i){
int l, r; cin >> l >> r;
query[r].emplace_back(l, i);
}
for(int i = 1; i <= n; ++i){
for(auto t : query[i]){
int l, idx; tie(l, idx) = t;
if(!idx) tr.add(l, 1);
else ans[idx] = tr.qry(n) - tr.qry(l - 1);
}
}
for(int i = 1; i <= q; ++i){
cout << (ans[i] == 0 ? "Yes" : "No") << '\n';
}
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2138C2 Maple and Tree Beauty
题目等价于给定 \(k\) 个 0,\(n - k\) 个 1,每一层只能染相同的颜色,为最多能染满前几层?当然这个答案不能超过最浅的叶子节点的深度 \(mxdep\)。
经典的可行性 dp,记第 \(i\) 层有 \(d_i\) 个点。考虑 \(f_{i, j}\) 表示染了前 \(i\) 层,用了 \(j\) 个 0。那么有转移 \(f_{i, j} \gets \begin{cases} f_{i - 1, j - d_i} & j \ge d_i \\ f_{i - 1, j} & n - j \ge d_i \end{cases}\)。
考虑 bitset 优化。直接做常数太大了,无法通过。发现随着层数的递增,我们要保证 1 的点不被用成负的,那么可能的最小的 \(j\) 是单调递增的。那么维护这个最小的位置 \(l\),再维护一个 bitset \(lim\) 是 \(\ge l\) 的全都是 1,小于 \(l\) 的全都是 0。那么每次从 \(i - 1 \to i\) 只需要,左移 \(d_{i}\) 位然后与上这个 \(lim\) 即可。这个 \(lim\) 的维护完美表达了第二种转移。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
bitset<N> f, leaf, lim;
int n, p[N], k, dep[N], cnt[N], mxdep;
void solve(){
cin >> n >> k;
dep[1] = 1;
leaf.set();
mxdep = n;
lim.reset();
for(int i = 0; i <= k; ++i) lim[i] = 1;
for(int i = 1; i <= n; ++i) cnt[i] = 0;
for(int i = 2; i <= n; ++i){
cin >> p[i];
dep[i] = dep[p[i]] + 1;
cnt[dep[i]]++, leaf[p[i]] = 0;
}
for(int i = 1; i <= n; ++i) if(leaf[i]) mxdep = (mxdep > dep[i] ? dep[i] : mxdep);
int nw = 0;
f.reset();
f[0] = 1;
int sum = 0, l = 0;
for(int i = 1; i <= mxdep; ++i){
nw ^= 1;
while(l < cnt[i] + sum - n + k) lim[l] = 0, ++l;
f = (f | (f << cnt[i])) & lim;
if(!f.any()) return cout << i - 1 << '\n', void();
sum += cnt[i];
}
cout << mxdep << '\n';
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
int T; cin >> T;
while(T--) solve();
return 0;
}
CF2138D Antiamuny and Slider Movement
推箱子的计数版本。下面在 \(a'_i \gets a_i - i\) 意义下考虑,算答案的时候加回来就好。
考虑一下推箱子操作对 \(i\) 的影响,对于一个操作 \((x, y)\)。有 \(a_i \gets \begin{cases} \max(a_i, y) & x < i \\ y & x = i \\ \min(a_i, y) & x > i \end{cases}\)。
考虑怎么把这个 \(max/min\) 去掉。一个经典 trick,我们考虑对最终 \(i\) 被移动到了 \(\ge j\) 的位置的方案数 \(f_j\) 进行计数,那么最后落在 \(j\) 的方案就是 \(f_j - f_{j + 1}\)。那么现在我们只关心位置与 \(j\) 的大小关系。那么我们认为 \(<j\) 的是 0,\(\ge j\) 的是 1。那么一次操作就变成了赋值 0 / 1 或者无影响。我们对最后一个有影响的操作是 1 计数方案即可,在知道了操作 0 / 1 / 无影响的个数之后(可以前缀和维护),这是 trivial 的(用插空的想法把无影响的操作插进去即可)。
离散化后复杂度 \(O(nq)\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5, mod = 1e9 + 7;
typedef long long ll;
ll fac[N], inv[N], s1[N], s2[N], s3[N], f[N], V[N];
int n, m, q, x[N], a[N], y[N], tot;
void add(ll &x, ll y){ (x += y) %= mod; }
void solve(){
cin >> n >> m >> q;
tot = 0;
for(int i = 1; i <= n; ++i) cin >> a[i], a[i] -= i, V[++tot] = a[i];
for(int i = 1; i <= q; ++i) cin >> x[i] >> y[i], y[i] -= x[i], V[++tot] = y[i];
sort(V + 1, V + 1 + tot);
tot = unique(V + 1, V + 1 + tot) - V - 1;
for(int i = 1; i <= n; ++i) a[i] = lower_bound(V + 1, V + 1 + tot, a[i]) - V;
for(int i = 1; i <= q; ++i) y[i] = lower_bound(V + 1, V + 1 + tot, y[i]) - V;
for(int i = 1; i <= n; ++i){
for(int j = 1; j <= tot; ++j) s1[j] = s2[j] = s3[j] = 0;
for(int j = 1; j <= q; ++j){
if(x[j] < i) s1[y[j]]++;
else if(x[j] > i) s2[y[j]]++;
else s3[y[j]]++;
}
for(int j = 1; j <= tot; ++j) s1[j] += s1[j - 1], s2[j] += s2[j - 1], s3[j] += s3[j - 1];
for(int j = 1; j <= tot; ++j){
f[j] = 0;
int s = s1[tot] - s1[j - 1] + s3[tot] - s3[j - 1], t = s2[j - 1] + s3[j - 1];// s = 1, t = 0
if(s == 0 && t == 0){
f[j] = (a[i] >= j) * fac[q];
continue;
}
else if(s == 0 && t > 0) continue;
f[j] = s * inv[s + t] % mod * fac[q] % mod;
}
ll ans = f[tot] * (V[tot] + i) % mod;
for(int j = 1; j < tot; ++j) add(ans, (mod + f[j] - f[j + 1]) * (V[j] + i) % mod);
cout << ans << ' ';
}
cout << '\n';
}
int main(){
cin.tie(nullptr)->sync_with_stdio(0);
fac[0] = fac[1] = inv[1] = 1;
for(int i = 2; i <= 5e3; ++i){
fac[i] = (fac[i - 1] * i) % mod;
inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
}
int T; cin >> T;
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号