Good Bye 2019 题解
Visits: 398
Card Game
谁有 $n$ 谁赢。
inline void solve() {
int n, a, b, x;
bool ok;
rd(n), rd(a), rd(b);
for (int i = 1; i <= a; i++) {
rd(x);
if (x == n) ok = 1;
}
for (int i = 1; i <= b; i++) {
rd(x);
if (x == n) ok = 0;
}
prints(ok ? "YES" : "NO");
}
int main() {
int T;
rd(T);
while (T--) solve();
return 0;
}
Interesting Subarray
显然如果存在要求的序列,必然存在长度为 $2$ 的要求的序列。
const int N = 2e5 + 7;
int n, a[N];
inline void solve() {
rd(n);
for (int i = 1; i <= n; i++) rd(a[i]);
bool ok = 0;
for (int i = 1; i < n; i++)
if (abs(a[i] - a[i+1]) > 1) ok = 1;
prints(ok ? "YES" : "NO");
if (ok) {
for (int i = 1; i < n; i++)
if (abs(a[i] - a[i+1]) > 1) {
print(i, ' '), print(i + 1);
break;
}
}
}
int main() {
int T;
rd(T);
while (T--) solve();
return 0;
}
Make Good
限定所有数异或起来等于一个很大的 $2$ 的幂,同时限定加上的三个数中有两个一样,就可以做了。
const int N = 1e5 + 7;
int n;
ll a[N], s, x;
inline void solve() {
rd(n), s = x = 0;
for (int i = 1; i <= n; i++) rd(a[i]), s += a[i], x ^= a[i];
ll w = 1ll << 54, k;
vector< ll > ans;
ans.pb(k = w ^ x);
w <<= 1;
w -= s + k;
ans.pb(w >> 1), ans.pb(w >> 1);
print(3), print(ans[0], ' '), print(ans[1], ' '), print(ans[2]);
}
int main() {
int T;
rd(T);
while (T--) solve();
return 0;
}
Strange Device
交互题。
首先可以通过 $n-k+1$ 次询问确定 $n-k+1$ 个位置的值。
若 $n – k + 1 \ge k$,则直接询问已知的 $k$ 个找到 $m$ 即可。
否则,考虑最后一次询问会产生一个已知的位置 $x$ 和 $k-1$ 个未知的位置,我们可以拿另外一个已知的位置 $y$ 分别替换这 $k-1$ 个未知的位置,然后根据回答与不替换时的回答是否一致判断这个位置与 $x$ 位置上的数的大小关系,即可找到这些数中有多少个比位置 $x$ 上的数小,从而求出 $m$。
const int N = 507;
int n, k, a[N];
inline pi ask(vi o) {
vi O = o;
sort(o.begin(), o.end());
cout << '?';
for (auto c : o) cout << ' ' << c;
cout << endl;
fflush(stdout);
int a, b;
cin >> a >> b;
o = O;
return mp(a, b);
}
int main() {
cin >> n >> k;
if (k == 1) return cout << "! 1" << endl, 0;
vi o, q;
for (int i = 1; i <= k; i++) o.pb(i);
for (int i = k; i <= n; i++) {
pi p = ask(o);
a[p.fi] = p.se;
q.pb(p.fi);
if (i != n)
for (ui j = 0; j < o.size(); j++)
if (o[j] == p.fi) o[j] = i + 1;
}
if (n - k + 1 >= k) {
o.clear();
for (int i = 0; i < k; i++) o.pb(q[i]);
pi p = ask(o);
int ans = 0;
for (int i = 0; i < k; i++)
if (a[o[i]] <= p.se) ++ans;
cout << "! " << ans << endl;
return 0;
}
int x = q.back();
q.pop_back();
int y = q.back();
for (ui i = 1; i < o.size(); i++)
if (o[i] == x) swap(o[0], o[i]);
int ans = 0;
for (ui i = 1; i < o.size(); i++) {
int z = o[i];
o[i] = y;
pi p = ask(o);
if (p.fi == x) ans += a[y] < a[x];
else ans += a[x] < a[y];
o[i] = z;
}
cout << "! " << ans + 1 << endl;
return 0;
}
Divide Points
并查集。
首先把必须在同一组的弄到一起,否则随便分成两组。
const int N = 1e3 + 7;
int n, f[N], t;
ll a[N], b[N];
pair< ll, int > p[N];
pair< ll, pi > q[N*N];
int get(int x) {
return x == f[x] ? x : (f[x] = get(f[x]));
}
inline ll S(int i, int j) {
return abs(a[i] - a[j]) * abs(a[i] - a[j]) + abs(b[i] - b[j]) * abs(b[i] - b[j]);
}
inline void work(int x) {
for (int i = 1; i <= n; i++) p[i] = mp(S(x, i), i);
sort(p + 1, p + n + 1);
for (int i = 1; i < n; i++)
if (p[i].fi == p[i+1].fi)
f[get(p[i].se)] = get(p[i+1].se);
}
int main() {
rd(n);
for (int i = 1; i <= n; i++) rd(a[i]), rd(b[i]), f[i] = i;
for (int i = 1; i <= n; i++) work(i);
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
q[++t] = mp(S(i, j), mp(i, j));
sort(q + 1, q + t + 1);
for (int i = 1; i < t; i++)
if (q[i].fi == q[i+1].fi) {
int j = i + 1;
int a1 = get(q[i].se.fi), b1 = get(q[i].se.se);
int a2 = get(q[j].se.fi), b2 = get(q[j].se.se);
if (a1 == a2) f[b1] = b2;
else if (a1 == b2) f[b1] = a2;
else if (b1 == a2) f[a1] = b2;
else if (b1 == b2) f[a1] = a2;
else if (a1 == b1) f[a2] = b2;
else if (a2 == b2) f[a1] = b1;
else f[a1] = a2, f[b1] = b2;
}
vi ans;
for (int i = 1; i <= n; i++)
if (get(i) == get(1)) ans.pb(i);
print(ans.size());
for (ui i = 0; i < ans.size(); i++) print(ans[i], ' ');
return 0;
}
Awesome Substrings
先求出前缀和 $s_{0\dots n}$,转化为求满足 $0 \le i < j \le n$ 且 $j – i = d(s_j – s_i)$ 的 $(i,j)$ 的个数,其中 $d$ 为 $\in [1,n]$ 的正整数。
考虑根号分治,给定一个限制 $T$,移项得 $s_jd – j = s_id – i$,设 $f_i(d) = s_id – i$。
对于 $1 \le d \le T$ 的情况,我们对每个 $d$ 求出 $f_{0\dots n}(d)$ 的值,然后对于每个值 $x$,若有 $k$ 个 $i$ 满足 $f_i(d) = x$,则对答案有 $\binom{k}2$ 的贡献。
这一部分实现优秀的话可达到 $\mathcal O(nT)$。
对于 $T < d \le n$ 的情况,由于 $s_j – s_i = \frac{j-i} d < \frac nT$,即有贡献的 $(a,b)$ 对应的区间中 $1$ 的个数 $k < \frac nT$。因此我们对每个 $i$ 和 $k$ 找到 $j$ 的范围 $(l,r]$,其对答案的贡献为 $\lfloor \frac {r – i}k \rfloor – \lfloor \frac {l – i}k \rfloor$ 去掉 $d \le T$ 的部分。
这一部分实现优秀的话可达到 $\mathcal O(n\frac nT)$。
因此总时间复杂度为 $\mathcal O(nT + n\frac nT)$,取 $T = \sqrt n$ 即可达到最优时间复杂度 $\mathcal O(n \sqrt n)$。
const int N = 2e5 + 7, M = 507;
int n, T, s[N], a[N*M];
char c[N];
ll ans;
int main() {
rds(c, n), T = sqrt(n);
for (int i = 1; i <= n; i++) s[i] = s[i-1] + c[i] - '0';
for (int d = 1, x; d <= T; d++) {
vi p;
for (int i = 0; i <= n; i++) x = s[i] * d - i + n, ++a[x], p.pb(x);
while (p.size()) x = p.back(), p.pop_back(), ans += 1ll * a[x] * (a[x] - 1) / 2, a[x] = 0;
}
for (int k = n / T - (T * T == n); k; k--) {
int l = 0;
while (l < n && s[l+1] < k) ++l;
if (l == n) continue;
int r = l + 1;
while (r < n && s[r+1] <= k) ++r;
for (int i = 0; i < n; i++) {
if (s[r] - s[i] < k) {
if (r == n) break;
l = r;
while (r < n && s[r+1] - s[i] <= k) ++r;
}
if ((r - i) / k > T) ans += (r - i) / k - max((l - i) / k, T);
}
}
print(ans);
return 0;
}
Subset with Zero Sum
由于 $i – n \le a_i \le i – 1$,所以 $1 \le i – a_i \le n$。
建立一张 $n$ 个点的有向图,对于每个点 $i$,连边 $i \to i – a_i$。
这张图中每个点的出度都为 $1$,因此这张图是一个基环内向森林。
可以发现对于任意一个环,环上的点对应的下标就是我们要找的答案。
const int N = 1e6 + 7;
int n, x, t[N], v[N];
inline void solve() {
rd(n);
for (int i = 1; i <= n; i++) rd(x), t[i] = i - x, v[i] = 0;
x = 1;
while (!v[x]) v[x] = 1, x = t[x];
vi ans;
ans.pb(x), x = t[x];
while (x != ans[0]) ans.pb(x), x = t[x];
print(ans.size());
while (ans.size()) print(ans.back(), " \n"[ans.back()==ans[0]]), ans.pop_back();
}
int main() {
int T;
rd(T);
while (T--) solve();
return 0;
}
Number of Components
题目太神仙了,先咕咕咕着。
Xor on Figures
题目太神仙了,先咕咕咕着。