Atcoder Grand Contest 25
Problem D. Choosing Points
考虑对于给定点集 SSS 和输入 D1,D2D_1,D_2D1,D2 ,计算一个大小不小于 S4\frac{S}{4}4S 的独立集 work(S,D1,D2)work(S,D_1,D_2)work(S,D1,D2) 。
对于 D1,D2D_1,D_2D1,D2 均为奇数的情况,可以直接对点集二分图染色。
对于 D1,D2D_1,D_2D1,D2 存在一个奇数的情况,不妨设为 D1D_1D1 ,可以对点集二分图染色,对得到的两个新点集 A,BA,BA,B 重新计算 work(A,D2),work(B,D2)work(A,D_2),work(B,D_2)work(A,D2),work(B,D2) ,取较大者。
对于 D1,D2D_1,D_2D1,D2 存在一个模 444 余 222 的数的情况,不妨设为 D1D_1D1 ,则 D1D_1D1 必定是两个奇数的平方和,同样可以对点集进行条纹染色,对得到的两个新点集 A,BA,BA,B 重新计算 work(A,D2),work(B,D2)work(A,D_2),work(B,D_2)work(A,D2),work(B,D2) ,取较大者。
否则,即 D1,D2D_1,D_2D1,D2 均为 444 的倍数,可以将点集分为四份,分别计算 work(T,D12,D22)work(T,\frac{D_1}{2},\frac{D_2}{2})work(T,2D1,2D2) ,合并得到的点集即可。
时间复杂度 O(N2LogN)O(N^2LogN)O(N2LogN) 。
实际上,通过上述过程,我们也可以发现对于单独的 DDD ,得到的图是一张二分图,因此,对 D1,D2D_1,D_2D1,D2 分别二分图染色,取四份中较大的一份即可。
时间复杂度 O(N3)O(N^3)O(N3) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> 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;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
vector <pair <int, int>> work(vector <pair <int, int>> input, int a) {
if (input.size() <= 1) return input;
vector <pair <int, int>> ans;
if (a % 2) {
int cnt[2] = {0, 0};
for (auto x : input) cnt[(x.first + x.second) & 1]++;
int home = cnt[0] > cnt[1] ? 0 : 1;
for (auto x : input) if ((x.first + x.second) % 2 == home) ans.push_back(x);
return ans;
}
if (a % 4) {
int cnt[2] = {0, 0};
for (auto x : input) cnt[x.first & 1]++;
int home = cnt[0] > cnt[1] ? 0 : 1;
for (auto x : input) if (x.first % 2 == home) ans.push_back(x);
return ans;
}
vector <pair <int, int>> nxt, tmp;
nxt.clear(); for (auto x : input) if (x.first % 2 == 0 && x.second % 2 == 0) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4); for (auto x : tmp) ans.emplace_back(x.first * 2, x.second * 2);
nxt.clear(); for (auto x : input) if (x.first % 2 == 0 && x.second % 2 == 1) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4); for (auto x : tmp) ans.emplace_back(x.first * 2, x.second * 2 + 1);
nxt.clear(); for (auto x : input) if (x.first % 2 == 1 && x.second % 2 == 0) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4); for (auto x : tmp) ans.emplace_back(x.first * 2 + 1, x.second * 2);
nxt.clear(); for (auto x : input) if (x.first % 2 == 1 && x.second % 2 == 1) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4); for (auto x : tmp) ans.emplace_back(x.first * 2 + 1, x.second * 2 + 1);
return ans;
}
vector <pair <int, int>> work(vector <pair <int, int>> input, int a, int b) {
if (input.size() <= 1) return input;
vector <pair <int, int>> ans;
if (a % 2 && b % 2) {
int cnt[2] = {0, 0};
for (auto x : input) cnt[(x.first + x.second) & 1]++;
int home = cnt[0] > cnt[1] ? 0 : 1;
for (auto x : input) if ((x.first + x.second) % 2 == home) ans.push_back(x);
return ans;
}
if (a % 2) swap(a, b);
if (b % 2) {
vector <pair <int, int>> nxt1, nxt2;
for (auto x : input) {
if ((x.first + x.second) & 1) nxt1.push_back(x);
else nxt2.push_back(x);
}
vector <pair <int, int>> tmp1 = work(nxt1, a);
vector <pair <int, int>> tmp2 = work(nxt2, a);
if (tmp1.size() > tmp2.size()) return tmp1;
else return tmp2;
}
if (a % 4) swap(a, b);
if (b % 4) {
vector <pair <int, int>> nxt1, nxt2;
for (auto x : input) {
if (x.first & 1) nxt1.push_back(x);
else nxt2.push_back(x);
}
vector <pair <int, int>> tmp1 = work(nxt1, a);
vector <pair <int, int>> tmp2 = work(nxt2, a);
if (tmp1.size() > tmp2.size()) return tmp1;
else return tmp2;
}
vector <pair <int, int>> nxt, tmp;
nxt.clear(); for (auto x : input) if (x.first % 2 == 0 && x.second % 2 == 0) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4, b / 4); for (auto x : tmp) ans.emplace_back(x.first * 2, x.second * 2);
nxt.clear(); for (auto x : input) if (x.first % 2 == 0 && x.second % 2 == 1) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4, b / 4); for (auto x : tmp) ans.emplace_back(x.first * 2, x.second * 2 + 1);
nxt.clear(); for (auto x : input) if (x.first % 2 == 1 && x.second % 2 == 0) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4, b / 4); for (auto x : tmp) ans.emplace_back(x.first * 2 + 1, x.second * 2);
nxt.clear(); for (auto x : input) if (x.first % 2 == 1 && x.second % 2 == 1) nxt.emplace_back(x.first / 2, x.second / 2);
tmp = work(nxt, a / 4, b / 4); for (auto x : tmp) ans.emplace_back(x.first * 2 + 1, x.second * 2 + 1);
return ans;
}
int main() {
int n, a, b; read(n), read(a), read(b);
vector <pair <int, int>> input;
for (int i = 0; i < n * 2; i++)
for (int j = 0; j < n * 2; j++)
input.emplace_back(i, j);
vector <pair <int, int>> res = work(input, a, b); int cnt = 0;
for (auto x : res) if (++cnt <= n * n) printf("%d %d\n", x.first, x.second);
return 0;
}
Problem E. Walking on a Tree
显然,记 cic_ici 表示第 iii 条边被覆盖的次数,显然答案存在上界 ∑imin{ ci,2}\sum_{i}\min\{c_i,2\}∑imin{ ci,2} 。
考虑构造方案取到该上界,对于叶子节点 iii ,令其父边被覆盖次数为 xxx 。
若 x≤1x\leq 1x≤1 ,显然,可以删去节点 iii 。
考虑 x≥2x\geq 2x≥2 的情况,任取两条覆盖该点的路径 (a,i),(i,b)(a,i),(i,b)(a,i),(i,b) 。
则若要使得 iii 被覆盖两次, (a,i),(i,b)(a,i),(i,b)(a,i),(i,b) 的方向必须不同。
考虑 (a,i),(i,b)(a,i),(i,b)(a,i),(i,b) 的交 (i,c)(i,c)(i,c) , (i,c)(i,c)(i,c) 上的边都被覆盖了两次,在后续过程中都不需要继续考虑。
考虑 (i,c)(i,c)(i,c) 外的部分,可以发现 a→c,c→ba\rightarrow c,c\rightarrow ba→c,c→b 等价于 a→ba\rightarrow ba→b ,而 b→c,c→ab\rightarrow c,c\rightarrow ab→c,c→a 等价于 b→ab\rightarrow ab→a ,因此可以加入 (a,b)(a,b)(a,b) 代替上述两条路径参与后续计算。
由此,我们构造了一种方案。
时间复杂度 O(N×(N+M))O(N\times (N+M))O(N×(N+M)) 。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e3 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> 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;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
vector <int> a[MAXN];
int depth[MAXN], father[MAXN];
int n, m, tot, ans, x[MAXN], y[MAXN];
bool used[MAXN], ok[MAXN], res[MAXN];
int nowx[MAXN], nowy[MAXN], f[MAXN], g[MAXN];
void dfs(int pos, int fa) {
depth[pos] = depth[fa] + 1;
father[pos] = fa;
for (auto x : a[pos])
if (x != fa) dfs(x, pos);
}
void mark(int a, int b, int c, int d) {
static int cnt[MAXN];
memset(cnt, 0, sizeof(cnt));
while (a != b) {
if (depth[a] < depth[b]) swap(a, b);
cnt[a]++, a = father[a];
}
while (c != d) {
if (depth[c] < depth[d]) swap(c, d);
cnt[c]++, c = father[c];
}
for (int i = 1; i <= n; i++)
ok[i] |= cnt[i] >= 2;
}
void work(int pos, int fa) {
for (auto x : a[pos])
if (x != fa) work(x, pos);
if (pos != 1) {
int cnt = 0;
for (int i = 1; i <= tot; i++)
if (!used[i]) cnt += (nowx[i] == pos) ^ (nowy[i] == pos);
ans += min(max(cnt, ok[pos] * 2), 2);
if (cnt <= 1 || ok[pos]) {
for (int i = 1; i <= tot; i++)
if (!used[i]) {
if (nowx[i] == pos) nowx[i] = fa;
if (nowy[i] == pos) nowy[i] = fa;
}
return;
}
vector <int> points;
for (int i = 1; i <= tot; i++)
if (!used[i] && ((nowx[i] == pos) ^ (nowy[i] == pos))) {
points.push_back(i);
}
int a = points[0], b = points[1];
mark(nowx[a], nowy[a], nowx[b], nowy[b]);
used[a] = used[b] = true;
if (nowx[a] == pos && nowx[b] == pos) {
tot++, f[tot] = -a, g[tot] = -b;
x[tot] = y[a], nowx[tot] = nowy[a];
y[tot] = y[b], nowy[tot] = nowy[b];
}
if (nowy[a] == pos && nowx[b] == pos) {
tot++, f[tot] = a, g[tot] = -b;
x[tot] = x[a], nowx[tot] = nowx[a];
y[tot] = y[b], nowy[tot] = nowy[b];
}
if (nowx[a] == pos && nowy[b] == pos) {
tot++, f[tot] = -a, g[tot] = b;
x[tot] = y[a], nowx[tot] = nowy[a];
y[tot] = x[b], nowy[tot] = nowx[b];
}
if (nowy[a] == pos && nowy[b] == pos) {
tot++, f[tot] = a, g[tot] = b;
x[tot] = x[a], nowx[tot] = nowx[a];
y[tot] = x[b], nowy[tot] = nowx[b];
}
for (int i = 1; i <= tot; i++)
if (!used[i]) {
if (nowx[i] == pos) nowx[i] = fa;
if (nowy[i] == pos) nowy[i] = fa;
}
}
}
int main() {
read(n), read(m), tot = m;
for (int i = 1; i <= n - 1; i++) {
int x, y; read(x), read(y);
a[x].push_back(y);
a[y].push_back(x);
}
for (int i = 1; i <= m; i++) {
read(x[i]), read(y[i]);
nowx[i] = x[i];
nowy[i] = y[i];
}
dfs(1, 0);
work(1, 0);
writeln(ans);
for (int i = tot; i >= 1; i--) {
if (!used[i]) res[i] = true;
if (i > m) {
int a = abs(f[i]), b = abs(g[i]);
res[a] = res[i] ^ (x[i] != x[a]);
res[b] = res[i] ^ (y[i] != y[b]);
}
}
for (int i = 1; i <= m; i++)
if (res[i]) printf("%d %d\n", x[i], y[i]);
else printf("%d %d\n", y[i], x[i]);
return 0;
}
Problem F. Addition and Andition
令 Si,TiS_i,T_iSi,Ti 表示两个高精度数从最低位开始的第 iii 位。
定义一次对 iii 的进位操作如下:
首先,令 Si=Ti=2S_i=T_i=2Si=Ti=2 。
若 Si=2S_i=2Si=2 或 Ti=2T_i=2Ti=2 ,将其变为 000 ,并对下一位 +1+1+1 ,令 i=i+1i=i+1i=i+1 ,否则,结束进位操作。
定义一次加法操作如下:
从大到小对于每一个 Si=T1=1S_i=T_1=1Si=T1=1 的 iii ,进行进位操作。
题目要求我们进行 KKK 次加法操作。
考虑如下过程,从大到小对于每一个 Si=T1=1S_i=T_1=1Si=T1=1 的 iii ,进行 KKK 次如下操作 (1)(1)(1) :
找到最小的 j≥ij\geq ij