Codeforces Round #715 (Div. 1)

Codeforces Round #715 (Div. 1)

A. Binary Literature

有三个\(01\)字符串,要么有至少两个字符串\(0\)的个数大于等于\(1\)的个数,要么至少有两个字符串\(1\)的个数大于\(0\)的个数,找到那两个字符串,那么取\(n\)\(0/1\),然后把剩下的补上去即可

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 200005;
int T, n, m, cnt1, cnt2, cnt3, t;
char s1[N], s2[N], s3[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

int main()
{
	T = read();
	while (T--)
	{
		n = read(), m = n << 1, cnt1 = cnt2 = cnt3 = 0;
		scanf("%s", s1), scanf("%s", s2), scanf("%s", s3);
		for (Re i = 0; i < m; ++i)
		{
			(s1[i] ^ 48) ? ++cnt1 : --cnt1;
			(s2[i] ^ 48) ? ++cnt2 : --cnt2;
			(s3[i] ^ 48) ? ++cnt3 : --cnt3;
		}
		t = (cnt1 >= 0) + (cnt2 >= 0) + (cnt3 >= 0);
		if (t >= 2)
		{
			if (cnt1 < 0) for (Re i = 0; i < m; ++i) s1[i] = s3[i];
			else if (cnt2 < 0) for (Re i = 0; i < m; ++i) s2[i] = s3[i];
		}
		else
		{
			if (cnt1 > 0) for (Re i = 0; i < m; ++i) s1[i] = s3[i];
			else if (cnt2 > 0) for (Re i = 0; i < m; ++i) s2[i] = s3[i];
		}
		t = (t >= 2) ? 49 : 48;
		int j = 0, k = 0;
		for (Re i = 0; i < n; ++i)
		{
			while (j < m && s1[j] ^ t) putchar(s1[j++]);
			while (k < m && s2[k] ^ t) putchar(s2[k++]);
			putchar(t);
			++j, ++k;
		}
		while (j < m) putchar(s1[j++]);
		while (k < m) putchar(s2[k++]);
		putchar('\n');
	}
	return 0;
}

B. Almost Sorted

由于它要满足\(a_{i+1} \geq a_{i}-1\),把一个合法序列按每次要递减时为开头分成若干段,每一段都是连续递减,且每次只减\(1\),直到减到上一段的结尾\(+1\),所以\(n\)个数构成的合法序列一共是\(2^{n}\)种,然后预处理下二进制,后面正常做就行

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 100005;
int T, n, a[N];
ll k, g[N];

inline ll read()
{
	char c = getchar();
	ll ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void write(int x)
{
	int num = 0;
	char sc[15];
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar(' ');
}

int main()
{
	T = read(), g[0] = g[1] = 1;
	for (Re i = 2; i <= 60; ++i) g[i] = g[i - 1] << 1;
	while (T--)
	{
		n = read(), k = read();
		if (g[n] && k > g[n])
		{
			puts("-1");
			continue;
		}
		for (Re i = 1; i <= n; ++i)
			if (!g[n - i] || g[n - i] >= k) a[i] = i;
			else
			for (Re j = i; j <= n; ++j)
				if (g[n - j] >= k)
				{
					for (Re l = i; l <= j; ++l) a[l] = i + j - l;
					i = j;
					break;
				}
				else k -= g[n - j];
		for (Re i = 1; i <= n; ++i) write(a[i]);
		putchar('\n');
	}
	return 0;
}

C. Complete the MST

可以先求得未知边的边权的异或和,令未知边中仅一条边的值为这个异或和,其他均为\(0\),这样一定是最优的,不选那条边权值为异或和的边时,选\(0\)一定满足最小生成树最优,在一定要选那条边权值为异或的边和即所有未知边的边都要选时,最小生成树是要边权加起来,这样也是最优的
容易发现在数据范围较大时,这条边权为异或值的边一定能不被选到,考虑如果可能要选到边权为异或和的边时,一定是在\(\frac{n \cdot (n-1)}{2} - m < n\)范围内,即此时未知边的个数不超过最小生成树的边数,这样就可能选中全部的未知边,发现此时的\(n\)较小,可以先选出\(m\)条边中可能用到的\(n-1\)条边,然后取出所有未知边,枚举哪条未知边的边权为权值异或和,复杂度为\(\Theta(m log\ m +n^{2})\),但此时\(n\)最大不超过\(633\),所以可以较快通过
\(\frac{n \cdot (n-1)}{2} - m \geq n\)时,此时一定可以让一条不选则的边的边权为异或和,所以就是要在所有未知边的基础上,使得选择到的\(m\)条边中的边权和尽量小,可以\(Kruskal\)去做,先求出所有未知边构成的并查集情况,然后把那\(m\)条边排序后按\(Kruskal\)的方法做,求出所有未知边构成的并查集情况,可以对于一个未知并查集情况的点,将这个点通过未知边拓展出去,直到不能再拓展了,但是这样暴力做是\(\Theta(n^{2})\)的,原因是一个点可能会被多条边指向,考虑链表优化,这样一个点如果被遍历过后,这个点将不会被其他点指向,总的复杂度是\(\Theta(m log\ m + n + m)\)

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 200005, M = 900;
struct edg
{
	int l, r, s;
}eg[N], egg[N << 1], a[M];
int n, m, s, t, cnt, fa[N], pr[N], nt[N];
bool b[M][M];
ll ans;
vector<int> gg[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline bool cmp(edg x, edg y)
{
	return x.s < y.s;
}

inline bool cmp1(edg x, edg y)
{
	return x.r < y.r;
}

inline int find_fa(int x)
{
	return (fa[x] ^ x) ? fa[x] = find_fa(fa[x]) : x;
}

inline int find_nt(int x)
{
	return (fa[nt[x]]) ? nt[x] = find_nt(nt[x]) : nt[x];
}

inline void dfs(int x, int y)
{
	fa[x] = y, nt[pr[x]] = nt[x], pr[nt[x]] = pr[x];
	int u = gg[x].size(), v = 0, w = find_nt(0);
	while (v < u && w <= n)
	{
		int uu = gg[x][v];
		while (w < uu) dfs(w, y), w = find_nt(w);
		if (w == uu) w = find_nt(w);
		++v;
	}
	while (w <= n) dfs(w, y), w = find_nt(w);
}

int main()
{
	n = read(), m = read();
	for (Re i = 1; i <= m; ++i) eg[i].l = read(), eg[i].r = read(), eg[i].s = read(), s ^= eg[i].s;
	sort(eg + 1, eg + m + 1, cmp);
	if (1ll * n * (n - 1) / 2 < 1ll * n + m)
	{
		ans = 1e14;
		sort(eg + 1, eg + m + 1, cmp);
		for (Re i = 1; i <= m; ++i) b[eg[i].l][eg[i].r] = 1;
		for (Re i = 1; i < n; ++i)
			for (Re j = i + 1; j <= n; ++j)
				if (!b[i][j] && !b[j][i]) a[++t].l = i, a[t].r = j;
		int mm = 0;
		for (Re i = 1; i <= n; ++i) fa[i] = i;
		for (Re i = 1, j = n - 1; i <= m; ++i)
		{
			int u = eg[i].l, v = eg[i].r;
			if (find_fa(u) ^ find_fa(v))
			{
				eg[++mm] = eg[i], fa[fa[u]] = fa[v], --j;
				if (!j) break;
			}
		}
		m = mm;
		for (Re i = 1; i <= t; ++i)
		{
			for (Re j = 1; j <= n; ++j) fa[j] = j;
			int w = n - 1;
			ll tot = 0;
			for (Re j = 1; j < i; ++j)
			{
				int u = a[j].l, v = a[j].r;
				if (find_fa(u) ^ find_fa(v))
				{
					fa[fa[u]] = fa[v], --w;
					if (!w)
					{
						putchar(48);
						return 0;
					}
				}
			}
			for (Re j = i + 1; j <= t; ++j)
			{
				int u = a[j].l, v = a[j].r;
				if (find_fa(u) ^ find_fa(v))
				{
					fa[fa[u]] = fa[v], --w;
					if (!w)
					{
						putchar(48);
						return 0;
					}
				}
			}
			for (Re j = 1; j <= m; ++j)
			{
				if (eg[j - 1].s <= s && eg[j].s > s && find_fa(a[i].l) ^ find_fa(a[i].r)) fa[fa[a[i].l]] = fa[a[i].r], --w, tot += s;
				int u = eg[j].l, v = eg[j].r;
				if (find_fa(u) ^ find_fa(v))
				{
					fa[fa[u]] = fa[v], --w, tot += eg[j].s;
					if (!w || tot >= ans) break;
				}
			}
			if (w) tot += s;
			if (tot < ans) ans = tot;
		}
	}
	else
	{
		for (Re i = 1; i <= m; ++i)
			egg[i] = eg[i], egg[i + m].l = eg[i].r, egg[i + m].r = eg[i].l;
		sort(egg + 1, egg + (m << 1) + 1, cmp1);
		for (Re i = 1; i <= (m << 1); ++i) gg[egg[i].l].push_back(egg[i].r);
		for (Re i = 0; i <= n + 1; ++i) pr[i] = i - 1, nt[i] = i + 1;
		for (Re i = 1; i <= n; ++i)
			if (!fa[i]) ++t, dfs(i, i);
		--t;
		if (!t)
		{
			putchar(48);
			return 0;
		}
		for (Re i = 1; i <= m; ++i)
		{
			int u = eg[i].l, v = eg[i].r;
			if (find_fa(u) ^ find_fa(v))
			{
				fa[fa[u]] = fa[v], ans += eg[i].s, --t;
				if (!t) break;
			}
		}
	}
	printf("%lld", ans);
	return 0;
}

D. Swap Pass

对于一个数列\(i,a_{i},a_{a_{i}}...\)最后一定能回到\(i\),称这个为一个循环,处理一个循环,只需要任意选中循环中的一个点,然后让这个点一直将它上面的\(a\)值换到正确的位置上即可,但是不同循环之间的操作可能会相交
考虑怎么把两个循环合并起来,只需要在他们之间连一条边,并且这条边不能与操作中的边有相交即可
考虑将所有循环合并起来,每次碰到一个新的循环就与原先的循环合并即可
考虑怎么使得合并循环的这些边与操作中的边不相交
取一个操作中心,其他点按与这个点的连边的斜率排序,合并两个循环时只要合并排序后相邻两个不同循环上的点即可
复杂度是\(\Theta(n log\ n)\)

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 2005;
struct info
{
	int x, y, t, id;
}a[N];
int n, t, res, fa[N], g[N], ansl[N * N], ansr[N * N];
bool b[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

inline void write(int x)
{
	if (x < 0) putchar('-'), x = -x;
	int num = 0;
	char sc[15];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
}

inline void dfs(int x, int y)
{
	fa[x] = y;
	if (x ^ y) b[y] = 1;
	if (!fa[a[x].t]) dfs(a[x].t, y);
}

inline int find_fa(int x)
{
	return (fa[x] ^ x) ? fa[x] = find_fa(fa[x]) : x;
}

inline bool cmp(info x, info y)
{
	return (b[fa[x.t]] ^ b[fa[y.t]]) ? b[fa[x.t]] > b[fa[y.t]] : 1ll * (x.y - a[1].y) * (y.x - a[1].x) > 1ll * (y.y - a[1].y) * (x.x - a[1].x);
}

int main()
{
	n = read();
	for (Re i = 1; i <= n; ++i) a[i].x = read(), a[i].y = read(), a[i].t = read(), a[i].id = i;
	for (Re i = 1; i <= n; ++i)
		if (!fa[i]) dfs(i, i);
	for (Re i = 1; i <= n; ++i)
		if (a[i].id ^ a[i].t && (!t || a[i].x < a[t].x || (a[i].x == a[t].x && a[i].y < a[t].y))) t = i;
	if (!t)
	{
		putchar(48);
		return 0;
	}
	if (t > 1) swap(a[1], a[t]);
	sort(a + 2, a + n + 1, cmp);
	for (Re i = 3; i <= n; ++i)
		if (b[fa[a[i].t]])
		{
			if (find_fa(a[i - 1].t) == find_fa(a[i].t)) continue;
			ansl[++res] = a[i - 1].id, ansr[res] = a[i].id, fa[fa[a[i - 1].t]] = fa[a[i].t], swap(a[i - 1].t, a[i].t);
		}
		else break;
	for (Re i = 2; i <= n; ++i) g[a[i].id] = i;
	while (a[1].id ^ a[1].t) ansl[++res] = a[1].id, ansr[res] = a[1].t, a[1].t = a[g[a[1].t]].t;
	printf("%d\n", res);
	for (Re i = 1; i <= res; ++i) write(ansl[i]), putchar(' '), write(ansr[i]), putchar('\n');
	return 0;
}

E. Tree Calendar

个人感觉思维难度比\(D\)简单,但是代码量比\(D\)大,而且细节较多
可以发现你再怎么操作,对于叶子节点,他们的相对大小是固定的,可以先找出最初的\(dfs\)序,并判断这个\(dfs\)序合不合法
记当前根节点的\(dfs\)序值为\(x\)
如果\(x=1\),和最初树的比较,看是否一样
否则,对于值\(1\)\(x-2\),一定是移动到不能移动,每次移动到的点的所有儿子的值一定是之前就移动到的值,并且假设当前移动的值为\(y\),则\(y\)移动到的位置一定是\(y-1\)移动到的位置的父亲节点所在的子树内
移完\(1\)\(x-2\)后可以找出当前所有点的\(dfs\)值,然后暴力移动\(x-1\),看能不能移动到你目标的\(dfs\)
如果满足题意是\(YES\)的话,移动的次数就是\(1~x-1\)每个点的深度之和再减去\(x-1\),因为每个点都是从深度为\(1\)的根节点开始移动的
时间复杂度为\(\Theta(n log\ n)\)

#include<bits/stdc++.h>
using namespace std;
#define Re register int
typedef long long ll;

const int N = 300005;
int n, cnt, t, tt, res, tim, hea[N], nxt[N << 1], to[N << 1], a[N], c[N], id[N], fa[N], dep[N], dfn_now[N], dfn_cur[N], dfn_ans[N];
bool b[N];
ll ans;
vector<int> son[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	bool f = 1; 
	while (c < 48 || c > 57)
	{
		if (c == '-') f = 0;
		c = getchar();
	}
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return f ? ans : -ans;
}

inline void write(ll x)
{
	int num = 0;
	char sc[25];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar(' ');
}

inline void add(int x, int y)
{
	nxt[++cnt] = hea[x], to[cnt] = y, hea[x] = cnt;
}

inline bool cmp_now(int x, int y)
{
	return dfn_now[x] < dfn_now[y];
}

inline bool cmp_ans(int x, int y)
{
	return dfn_ans[x] < dfn_ans[y];
}

inline void dfs(int x)
{
	dep[x] = dep[fa[x]] + 1, b[x] = 1;
	for (Re i = hea[x]; i; i = nxt[i])
	{
		int u = to[i];
		if (u == fa[x]) continue;
		fa[u] = x, b[x] = 0;
		dfs(u);
	}
	if (b[x]) a[++res] = x;
}

inline void dfs_check(int x)
{
	++tim;
	if (dfn_ans[x] ^ tim)
	{
		puts("NO");
		exit(0);
	}
	int u = 0;
	for (Re i = hea[x]; i; i = nxt[i])
		if (to[i] ^ fa[x]) c[++u] = to[i];
	sort(c + 1, c + u + 1, cmp_ans);
	for (Re i = 1; i <= u; ++i) son[x].push_back(c[i]);
	for (Re i = 0; i < u; ++i) dfs_check(son[x][i]);
}

inline void dfs1(int x)
{
	dfn_cur[x] = ++tim;
	int u = son[x].size();
	for (Re i = 0; i < u; ++i) dfs1(son[x][i]);
}

int main()
{
	n = read();
	for (Re i = 1; i <= n; ++i) dfn_now[i] = read(), id[dfn_now[i]] = i;
	for (Re i = 1; i < n; ++i)
	{
		int u = read(), v = read();
		add(u, v), add(v, u);
	}
	dfs(1);
	sort(a + 1, a + res + 1, cmp_now);
	for (Re i = 1; i <= res; ++i)
	{
		cnt = 0;
		for (Re j = a[i]; j && !dfn_ans[j]; j = fa[j]) c[++cnt] = j;
		for (Re j = cnt; j; --j) dfn_ans[c[j]] = ++tim;
	}
	tim = 0;
	dfs_check(1);
	if (dfn_now[1] == 1)
	{
		for (Re i = 2; i <= n; ++i)
			if (dfn_now[i] ^ dfn_ans[i])
			{
				puts("NO");
				return 0;
			}
		puts("YES");
		puts("0");
		for (Re i = 1; i <= n; ++i) write(dfn_ans[i]);
	}
	t = 1;
	for (Re i = 1, j = 0; i < dfn_now[1] - 1; ++i)
	{
		int u = id[i], v = son[u].size();
		if (dep[u] < dep[t])
		{
			puts("NO");
			return 0;
		}
		for (Re k = 0; k < v; ++k)
			if (!dfn_cur[son[u][k]])
			{
				puts("NO");
				return 0;
			}
		if (b[u]) ++j;
		if (b[u] && u ^ a[j])
		{
			puts("NO");
			return 0;
		}
		ans += dep[u] - 1, dfn_cur[u] = i, t = fa[u];
	}
	cnt = 0, tim = dfn_now[1] - 2;
	for (Re i = t; i; i = fa[i]) c[++cnt] = i;
	for (Re i = cnt; i; --i) dfn_cur[c[i]] = ++tim;
	for (Re i = 1; i <= cnt; ++i)
	{
		int u = son[c[i]].size();
		for (Re j = 0; j < u; ++j)
			if (!dfn_cur[son[c[i]][j]]) dfs1(son[c[i]][j]);
	}
	t = dep[id[dfn_now[1] - 1]], tt = 1, ans += t - 1;
	while (dep[tt] < t)
	{
		int u = son[tt].size();
		bool pd = 0;
		for (Re i = 0; i < u; ++i)
			if (dfn_cur[son[tt][i]] > dfn_cur[tt])
			{
				swap(dfn_cur[tt], dfn_cur[son[tt][i]]), tt = son[tt][i], pd = 1;
				break;
			}
		if (!pd)
		{
			puts("NO");
			return 0;
		}
	}
	for (Re i = 1; i <= n; ++i)
		if (dfn_now[i] ^ dfn_cur[i])
		{
			puts("NO");
			return 0;
		}
	puts("YES");
	write(ans), putchar('\n');
	for (Re i = 1; i <= n; ++i) write(dfn_ans[i]);
	return 0;
}

F. Optimal Encoding

这题题意就很绕,知道完题意后感觉更绕了。。
正解似乎挺难的,在那场比赛的题解下面讨论区看到了ecnerwala大神比较简单且好写的做法,后面又去看了yhx大神超级简短的代码
对于点\(i\),它往左的边最多就两条,一条指向前驱,一条指向后继,往右的边也一样
对于一个区间\([l,r]\)中的某个点\(i\)来说,可以看成是两个区间\([l,i]\)\([i,r]\),又因为一条边是相互的,所以仅看成\([l,i]\)即可
对与\(i\)这个点以及区间\([l,i]\),从\(i+1\)开始往\(l\)判断这条边是否需要连接
对于点对\((i,j)\),判断它们是否需要连接判断一次即可,所以最多只会进行\(n^{2}\)级别的的判断操作
如果要连边当且仅当这条边这两个点的值都会更改对方的左边/右边的前驱/后继值
由于边是相互的,模拟过程可以发现,判断只要判断单向的就可以了
复杂度是\(\Theta(n^{2})\)

#include<bits/stdc++.h>
using namespace std;
#define Re register int

const int N = 25005;
int n, m, ans, a[N], d[N], lpre[N], lsuf[N], rpre[N], rsuf[N];

inline int read()
{
	char c = getchar();
	int ans = 0;
	while (c < 48 || c > 57) c = getchar();
	while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();
	return ans;
}

inline void write(int x)
{
	int num = 0;
	char sc[15];
	if (!x) sc[num = 1] = 48;
	while (x) sc[++num] = x % 10 + 48, x /= 10;
	while (num) putchar(sc[num--]);
	putchar('\n');
}

inline void add1(int x, int y)
{
	++ans, rsuf[x] = y, lpre[y] = x;
}

inline void add2(int x, int y)
{
	++ans, rpre[x] = y, lsuf[y] = x;
}

inline void cut1(int x, int y)
{
	ans -= (rsuf[x] == y && lpre[y] == x);
}

inline void cut2(int x, int y)
{
	ans -= (rpre[x] == y && lsuf[y] == x);
}

inline void calc(int x, int y)
{
	if (a[x] < a[y])
	{
		if (a[x] > a[lpre[y]]) cut1(lpre[y], y), cut1(x, rsuf[x]), add1(x, y);
	}
	else
	{
		if (a[x] < a[lsuf[y]]) cut2(lsuf[y], y), cut2(x, rpre[x]), add2(x, y);
	}
}

int main()
{
	n = read(), m = read(), a[n + 1] = n + 1;
	for (Re i = 1; i <= n; ++i) a[i] = read(), d[i] = i, lsuf[i] = rsuf[i] = n + 1;
	while (m--)
	{
		int u = read(), v = read();
		int l = u, r = v, s = v + 1;
		while (l <= r)
		{
			int mid = l + r >> 1;
			if (d[mid] > u) s = mid, r = mid - 1;
			else l = mid + 1;
		}
		for (Re i = s; i <= v; ++i)
			while (d[i] > u) calc(--d[i], i);
		write(ans);
	}
	return 0;
}
posted @ 2021-04-28 22:45  clfzs  阅读(161)  评论(0)    收藏  举报