校内训练 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;
}
posted @ 2019-10-05 10:16  hankeke303  阅读(162)  评论(0编辑  收藏  举报