校内训练 2019-10-04
这一场的 T1T2 有点简单,大概是 D1T1 和 D1T2 的难度。
T3 的奇怪的条件看上去就没有任何想法。\(2\) 的整次幂的条件,可以暗示很多算法,比如二进制分组、倍增、分治等。但是每一种似乎都无法使用。大约比 D2T2 的难度要大一些吧。
最后 T3 的标算的想法也很妙。通过发现最大度数为 \(1\) 的点集一定可以用 \(1\) 中颜色覆盖,从而想到分治的思路。
T1. cipele
题库传送门
http://192.168.21.187/contest/32/problem/1 = http://192.168.21.187/problem/1313
http://47.100.137.146/contest/32/problem/1 = http://47.100.137.146/problem/1313
题解
排序+二分+贪心+双指针。
没什么好说的。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<ll, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 1e5 + 7;
int n, m;
int a[N], b[N];
inline bool check(const int &mid) {
for (int i = 1, j = 1; i <= n; ++i) {
while (j < m && a[i] - b[j] > mid) ++j;
if (j > m || std::abs(a[i] - b[j]) > mid) return 0;
++j;
}
return 1;
}
inline void work() {
if (n > m) {
std::swap(n, m);
for (int i = 1; i <= m; ++i) std::swap(a[i], b[i]);
}
std::sort(a + 1, a + n + 1);
std::sort(b + 1, b + m + 1);
int l = 0, r = std::max(a[n] - b[1], b[m] - a[1]);
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
printf("%d\n", l);
}
inline void init() {
read(n), read(m);
for (int i = 1; i <= n; ++i) read(a[i]);
for (int i = 1; i <= m; ++i) read(b[i]);
// dbg("**********\n");
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#else
File(cipele);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
T2. strah
题目传送门
http://192.168.21.187/contest/32/problem/2 = http://192.168.21.187/problem/1314
http://47.100.137.146/contest/32/problem/2 = http://47.100.137.146/problem/1314
题解
首先把题目转化成所有矩形的覆盖格子总数。
我们考虑统计所有以 \((i, j)\) 为右上角的矩形的贡献。我们令 \(a_j\) 表示从 \((i, j)\) 这个点向下,有多少个适合种草莓的格子。
那么,以 \((i, j_1)\) 为左上角,\((i, j_2)\) 为右上角的矩形的最大高度为 \(\min\{a_j\mid j_1 \leq j \leq j_2 \}\)。另外,一个长度为 \(l\),最大高度为 \(h\) 的矩形,它的的总贡献是 \(\frac{h(h+1)l}{2}\)。
在统计所有以 \((i, j)\) 为右上角的矩形的贡献时,可以发现对于 \(j_1 \leq j\) 的矩形,最大高度被分成了很多段,每一段的内容就是目前的后缀最小值。
显然这个可以用单调栈维护。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<ll, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 2e3 + 7;
int n, m;
int a[N], q[N];
char s[N][N];
inline void work() {
for (int j = 1; j <= m; ++j)
for (int i = 1; i <= n && s[i][j]; ++i) ++a[j];
ll ans = 0;
for (int i = 1; i <= n; ++i) {
ll sum = 0, hs = 0;
int tl = 0;
for (int j = 1; j <= m; ++j) {
while (tl && a[j] <= a[q[tl]]) sum -= (a[q[tl]] + 1ll) * a[q[tl]] / 2 * (j - q[tl] + j - 1ll - q[tl - 1]) * (q[tl] - q[tl - 1]) / 2, hs -= (a[q[tl]] + 1ll) * a[q[tl]] / 2 * (q[tl] - q[tl - 1]), --tl;
hs += (a[j] + 1ll) * a[j] / 2 * (j - q[tl]), sum += (a[j] + 1ll) * a[j] / 2 * (1 + j - 1ll - q[tl]) * (j - q[tl] - 1) / 2, q[++tl] = j;
sum += hs, ans += sum;
// dbg("i = %d, j = %d, a[j] = %d, sum = %lld, hs = %lld\n", i, j, a[j], sum, hs);
}
for (int j = 1; j <= m; ++j) --a[j];
for (int j = 1; j <= m; ++j) if (!s[i][j]) {
a[j] = 0;
for (int k = i + 1; k <= n && s[k][j]; ++k) ++a[j];
}
}
printf("%lld\n", ans);
}
inline void init() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%s", s[i] + 1);
for (int j = 1; j <= m; ++j) s[i][j] = s[i][j] == '.';
}
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#else
File(strah);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
T3. teoreticar
题目传送门
http://192.168.21.187/contest/32/problem/3 = http://192.168.21.187/problem/1315
http://47.100.137.146/contest/32/problem/3 = http://47.100.137.146/problem/1315
题解
令 \(C\) 为这个二分图的所有点的最大度数。
如果 \(C = 1\),很容易发现只需要 \(1\) 中颜色即可完成染色。
如果 \(C > 1\),那么我们考虑把这个二分图的边集分成两部分,每一部分的点的最大度数都不超过 \(\lceil \frac C2 \rceil\)。对于二分图中的一个环,因为是二分图,所以环长一定为偶数,又因为环中每一个点一定连接两条边,所以我们在环上间隔着分组。即第一条便分到第一组,第二条边分到第二组,第三条边分到第三组……
如果还有一些不成环的东西形成了一棵树。当然我们对于树的话可以随便分,只需要保持一个点的所有出边都被均匀地分到了两组即可。不过为了方便,我们考虑把二分图中左边的所有的度数为奇数的点向一个虚点连边,右边的度数为奇数的点向另一个虚点连边。如果虚点的度数为奇数,那么就把两个虚点之间再连一条边。这样,这个图中就全部是环了,可以直接用上面的方法分组。
这样,任何一个点的出边都被均匀地分到了两个组中。对于度数为奇数的点,由于虚边的存在,所以可能不均匀,因此分治以后的最大度数需要加上上取整符号,即 \(\lceil \frac C2 \rceil\)。
从这个算法中可以看出,我们求得的答案不超过 \(2^{\lceil\log_2C\rceil}\)。又由于标准答案肯定不会小于 \(C\),所以我们求得的答案符合题意。
事实上,我们可以证明,标准答案一定恰好等于 \(C\),即二分图中最大度数。但是这个结论对于解决这个题目没有帮助,所以感兴趣的同学可以参考 EI 神仙的博客:https://blog.csdn.net/EI_Captain/article/details/89408037 。
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
const int N = 2e5 + 7;
const int M = 5e5 + 7;
int n, nl, nr, m, tcl = 2;
int deg[N], ans[M], vis[N], bl[M << 1];
struct Edges { int x, y, id; } c[M], d1[M], d2[M];
struct Edge { int to, ne, w; } g[M << 1]; int head[N], tot;
inline void addedge(int x, int y, int z) { g[++tot].to = y, g[tot].w = z, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y, int z) { addedge(x, y, z), addedge(y, x, z); }
inline void dfs(int x, int fa = 0) {
for fec(i, x, y) if (y != fa && !bl[g[i].w]) {
bl[g[i].w] = tcl ^= 1;//, dbg("nxt: %d %d %d\n", x, y, tcl);
head[x] = g[i].ne; dfs(y, x); return;
}
}
inline void calc(int l, int r) {
tot = vis[n + 1] = vis[n + 2] = head[n + 1] = head[n + 2] = 0;
// memset(bl, 0, sizeof(bl));
// memset(head, 0, sizeof(head));
// memset(deg, 0, sizeof(deg));
for (int i = l; i <= r; ++i) head[c[i].x] = head[c[i].y] = deg[c[i].x] = deg[c[i].y] = vis[c[i].x] = vis[c[i].y] = bl[c[i].id] = 0;
for (int i = l; i <= r; ++i) adde(c[i].x, c[i].y, c[i].id), ++deg[c[i].x], ++deg[c[i].y];//, dbg("edges: %d %d\n", c[i].x, c[i].y);
int mm = m, mxd = 0, dd = 0;
for (int i = l; i <= r; ++i) {
smax(mxd, deg[c[i].x]), smax(mxd, deg[c[i].y]);
// if (deg[c[i].x] == mxd) dbg("*** mxd: x = %d\n", c[i].x);
// if (deg[c[i].y] == mxd) dbg("*** mxd: y = %d\n", c[i].y);
if (deg[c[i].x] & 1) adde(c[i].x, n + 1, ++mm), ++dd;
if (deg[c[i].y] & 1) adde(c[i].y, n + 2, ++mm);
deg[c[i].x] = deg[c[i].y] = 0;
}
// dbg("l = %d, r = %d, mxd = %d\n", l, r, mxd);
if (dd & 1) adde(n + 1, n + 2, ++mm);
if (mxd == 1) {
++ans[0];
for (int i = l; i <= r; ++i) ans[c[i].id] = ans[0];
return;
}
// memset(bl, 0, sizeof(bl));
for (int i = m + 1; i <= mm; ++i) bl[i] = 0;
for (int i = l; i <= r; ++i) if (!vis[c[i].x]) dfs(c[i].x);
// dbg("******************************\n");
int totl = 0, totr = 0, tot = l - 1;
for (int i = l; i <= r; ++i) if (/*dbg("bl[c[%d].id] = %d\n", i, bl[c[i].id]), */bl[c[i].id] == 2) d1[++totl] = c[i]; else d2[++totr] = c[i];
for (int i = 1; i <= totl; ++i) c[++tot] = d1[i];
for (int i = 1; i <= totr; ++i) c[++tot] = d2[i];
calc(l, l + totl - 1), calc(l + totl, r);
}
inline void work() {
calc(1, m);
printf("%d\n", ans[0]);
for (int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
inline void init() {
read(nl), read(nr), read(m);
n = nl + nr;
int x, y;
for (int i = 1; i <= m; ++i) read(x), read(y), c[i] = (Edges){ x, y + nl, i };
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#else
File(teoreticar);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}