Codeforces Round #545 (Div. 2) 题解

Dashboard - Codeforces Round #545 (Div. 2) - Codeforces
C o d e f o r c e s   R o u n d   # 545   ( D i v .   2 ) \rm Codeforces~Round~\#545~(Div.~2) Codeforces Round #545 (Div. 2)

A Sushi for Two(黄)

sol

从左往右扫描这个 01 串,并记录当前段的长度和前一段的长度,假如出现了一个新段的话(前提是下一个字符和当前字符不同),我们就计算出当前的答案并更新最优解(显然,当前的答案是当前段长度和前一段长度最小值的两倍)。

时间复杂度 O ( n ) \mathcal O(n) O(n)

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-')
			f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

int n, ans, a, lst;

int cnt1, cnt2;

signed main()
{
	n = read();
	lst = read();
	lst == 1 ? cnt1++ : cnt2++;
	for(int i = 2; i <= n; ++i)
	{
		a = read();
		if(a != lst)
		{
			ans = max(ans, min(cnt1, cnt2));
			lst == 1 ? cnt2 = 1 : cnt1 = 1;
			lst = a;
		}
		else
			lst == 1 ? cnt1++ : cnt2++;
	}
	ans = max(ans, min(cnt1, cnt2));
	printf("%d", ans << 1);
	return 0;
}

B Circus(蓝)

sol

设每个人 i i i 的两个数分别为 a i , b i a_i,b_i ai,bi

考虑用两个数的和来表示,那么有 3 3 3 种情况即和分别为 0 , 1 , 2 0,1,2 0,1,2

设你选的位于第一部分的 n 2 \frac{n}{2} 2n 个人中这些情况的个数分别为 c 0 , c 1 , c 2 c0,c1,c2 c0,c1,c2

s u m sum sum 表示所有人 b i b_i bi 中为 1 1 1 的数的个数。

那么有 c 0 × 0 + c 1 × 1 + c 2 × 2 = s u m c0\times 0+c1\times 1+c2 \times 2=sum c0×0+c1×1+c2×2=sum c 0 + c 1 + c 2 = n 2 c0+c1+c2=\frac{n}{2} c0+c1+c2=2n

c 1 + c 2 × 2 = s u m c1+c2\times 2=sum c1+c2×2=sum c 0 + c 1 + c 2 = n 2 c0+c1+c2=\frac{n}{2} c0+c1+c2=2n

O ( n ) \mathcal O(n) O(n) 枚举 c 2 c2 c2 的取值,求出 c 0 c0 c0 c 1 c1 c1,然后判断即可。

时间复杂度 O ( n ) \mathcal O(n) O(n)

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-')
			f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

int n, sum;

char a[5007], b[5007];

vector<int> g0, g1, g2;

signed main()
{
	n = read();
	scanf("%s %s", a + 1, b + 1);
	for(int i = 1; i <= n; ++i)
	{
		sum += b[i] - '0';
		if(a[i] == '0' && b[i] == '0')
			g0.push_back(i);
		else if((a[i] == '0' && b[i] == '1') || (a[i] == '1' && b[i] == '0'))
			g1.push_back(i);
		else
			g2.push_back(i);
	}
	int c0, c1;
	for(int c2 = 0, len = g2.size(); c2 <= len; ++c2)
	{
		c1 = sum - c2 * 2, c0 = n / 2 - c2 - c1;
		if(c0 >= 0 && c0 <= g0.size() && c1 >= 0 && c1 <= g1.size())
		{
			for(int j = 0; j < c0; ++j)
				printf("%d ", g0[j]);
			for(int j = 0; j < c1; ++j)
				printf("%d ", g1[j]);
			for(int j = 0; j < c2; ++j)
				printf("%d ", g2[j]);
			return 0;
		}
	}
	printf("%d", -1);
	return 0;
}

C Skyscrapers(蓝)

sol

对于每一行和每一列先分别离散,记录每个位置在离散后的值,然后合并之后取较大的那个,然后剩下的部分顺次编号就行了。

时间复杂度 O ( n 2 log ⁡ n ) \mathcal O(n^2\log n) O(n2logn)

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-')
			f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

const int _ = 1007;

int n, m;

int l[_], r[_]; // l:lie r:hang

int a[_][_], b[_][_], c[_][_]; // b:hang c:lie

int S[_], top;

inline int max2(int a, int b, int c, int d)
{
	return max(max(a, b), max(c, d));
}

signed main()
{
	n = read(), m = read();
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j)
			a[i][j] = read();
	for(int i = 1; i <= n; ++i)
	{
		top= 0 ;
		for(int j = 1; j <= m; ++j)
			S[++top] = a[i][j];
		sort(S + 1, S + top + 1);
		top = unique(S + 1, S + top + 1) - S - 1;
		for(int j = 1; j <= m; ++j)
			b[i][j] = lower_bound(S + 1, S + top + 1, a[i][j]) - S;
		r[i] = top;
	}
	for(int i = 1; i <= m; ++i)
	{
		top = 0;
		for(int j = 1; j <= n; ++j)
			S[++top] = a[j][i];
		sort(S + 1, S + top + 1);
		top = unique(S + 1, S + top + 1) - S - 1;
		for(int j = 1; j <= n; ++j)
			c[j][i] = lower_bound(S + 1, S + top + 1, a[j][i]) - S;
		l[i] = top;	
	}
	for(int i = 1; i <= n; ++i)
	{
		for(int j = 1; j <= m; ++j)
			printf("%d ", max2(r[i], l[j], b[i][j] + l[j] - c[i][j], c[i][j] + r[i] - b[i][j]));
		printf("\n");
	}
	return 0;
}

D Camp Schedule(绿)

sol

要想出现的次数尽可能多,那么就要重复的利用,哪一部分是可以重复利用的呢?

就是前缀和后缀相同的部分,容易想到了 KMP 中 的 n e x t next next 就是求这个东西的。

那么我们对于 T T TKMP 然后先建一次 T T T,然后每次跳到 n e x t next next 接着往后放就行了。

当然,这里只需用到 n e x t ∣ T ∣ next_{|T|} nextT,也可以用哈希。

时间复杂度为 O ( ∣ S ∣ ) \mathcal O(|S|) O(S)

#include <bits/stdc++.h>

using namespace std;

char s[500005], t[500005], ans[500005];

int nxt[500005];

signed main()
{
	scanf("%s", s + 1), scanf("%s", t + 1);
	nxt[1] = 0;
	int len = strlen(t + 1);
	for (int i = 2, j = 0; i <= len; i++)
	{
		while (j && t[i] != t[j + 1])
			j = nxt[j];
		if (t[i] == t[j + 1])
			j++;
		nxt[i] = j;
	}
	int cnt1 = 0, cnt0 = 0, lens = strlen(s + 1), lent = strlen(t + 1);
	for (int i = 1; i <= lens; i++)
		s[i] == '0' ? cnt0++, 0 : cnt1++, 0;
	for (int i = 1, j = 1; i <= lens; i++, j++)
	{
		if (t[j] == '0' && !cnt0)
			ans[i] = '1', cnt1--;
		else if (t[j] == '1' && !cnt1)
			ans[i] = '0', cnt0--;
		else
		{
			ans[i] = t[j];
			t[j] == '0' ? cnt0-- : cnt1--;
		}
		if (j == lent)
			j = nxt[j];
	}
	printf("%s\n", ans + 1);
	return 0;
}

E Museums Tour(紫)

sol

考虑到每一天的博物馆开放情况是不同的,那么把每一个博物馆一周的每一天的情况存下来, ( u , x ) = u + ( x − 1 ) × n (u,x)=u+(x-1)\times n (u,x)=u+(x1)×n,即第 u u u 个博物馆第 x x x 天的编号是 u + ( x − 1 ) n u+(x-1)n u+(x1)n

然后连边,对于原图上的一条边 ( u , v ) (u,v) (u,v),连边 ∀ x ∈ [ 1 , d ] , ( u , x ) → ( v , x   m o d   d + 1 ) \forall x \in[1,d],(u,x) \to (v,x\bmod d+1) x[1,d],(u,x)(v,xmodd+1)

由于图上有环,考虑 tarjan 缩点,记录每一个 scc 开启的不同博物馆个数。

最后跑一边最长路即可。

时空复杂度为 O ( n d ) \mathcal O(nd) O(nd)

#pragma GCC optimize(2)
#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		x = x * 10 + c - '0', c = getchar();
	return x * f;
}

inline void write(int x)
{
	if (x < 0)
	{
		putchar('-');
		x = -x;
	}
	if (x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

const int _ = 1e5 + 10, N = 5e6 + 250;

int n, m, k;
long long sta[_];

struct edge
{
	int to, nxt;
} e[N], e2[N];

int tot, head[N], tot2, head2[N];

inline void add(int u, int v)
{
	e[++tot] = {v, head[u]};
	head[u] = tot;
}

inline void add2(int u, int v)
{
	e2[++tot2] = {v, head2[u]};
	head2[u] = tot2;
}

int idx, scc, bel[N], dfn[N], low[N];
int f[N], val[N];
bool vis[N];
stack<int> s, q;

inline void tarjan(int u)
{
	dfn[u] = low[u] = ++idx;
	s.push(u);
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (!bel[v])
		{
			low[u] = min(low[u], dfn[v]);
		}
	}
	if (low[u] == dfn[u])
	{
		++scc;
		while (1)
		{
			int v = s.top();
			s.pop();
			bel[v] = scc;
			int x = (v - 1) % n + 1, day = (v - 1) / n;
			if (!vis[x] && (sta[x] >> day & 1))
			{
				val[scc]++, vis[x] = 1;
				q.push(x);
			}
			if (v == u)
				break;
		}
		while (!q.empty())
			vis[q.top()] = 0, q.pop();
	}
}

inline int P(int i, int j)
{
	return j * n + i;
}

int dfs(int u)
{
	if (vis[u])
		return f[u];
	vis[u] = 1;
	for (int i = head2[u]; i; i = e2[i].nxt)
	{
		int v = e2[i].to;
		f[u] = max(f[u], dfs(v));
	}
	return f[u] += val[u];
}

int main()
{
	n = read(), m = read(), k = read();
	int u, v;
	for (int i = 1; i <= m; ++i)
	{
		u = read(), v = read();
		for (int j = 0; j < k; ++j)
			add(P(u, j), P(v, (j + 1) % k));
	}
	char ch;
	for (int i = 1; i <= n; ++i)
		for (int j = 0; j < k; ++j)
		{
			while (!isdigit(ch = getchar()))
				;
			sta[i] |= 1ll * (ch - '0') << j;
		}
	tarjan(1);
	for (int i = 1; i <= n * k; ++i)
	{
		if (!dfn[i])
			continue;
		u = bel[i];
		for (int j = head[i]; j; j = e[j].nxt)
		{
			v = e[j].to;
			if (!dfn[v])
				continue;
			v = bel[v];
			if (u != v)
				add2(u, v);
		}
	}
	printf("%d", dfs(bel[1]));
	return 0;
}

F Cooperative Game(紫)

sol

一道好题,用到了一点 pollard-rho 的思想。

考虑 Floyd 判圈法。

两个人同时在一个环形跑道上起跑,一个人是另外一个人的两倍速度,那么这个人绝对会追上另外一个人。追上时,意味着其中一个人已经跑了一圈了。

这样就可以知道环的长度了。

由上面的方法,我们可以知道,慢人一定还没走完一圈,就被追上了。

以下注: t t t 为链长, c c c 为环长。

所以先把 0 , 1 0,1 0,1 拿出来。

0 0 0 为慢点, 1 1 1 为快点。

假设现在 0 , 1 0,1 0,1 相遇。

那么设 0 0 0 走了 t + k t+k t+k 步,则 1 1 1 走了 2 t + 2 k 2t+2k 2t+2k 步( 0 ≤ k < c 0\leq k < c 0k<c)。

忽略在链上的 t t t 步,则 0 0 0 在环上走了 k k k 步, 1 1 1 在环上走了 t + 2 k t+2k t+2k 步。

于是有 t ≡ t + 2 k ( m o d c ) t \equiv t+2k \pmod c tt+2k(modc),即 t + k ≡ 0 ( m o d c ) t+k\equiv 0 \pmod c t+k0(modc),即 t ≡ c − k ( m o d c ) t \equiv c-k \pmod c tck(modc)

当前这两个人所处的位置再走 c − k c-k ck 步恰好是目标位置,而由于 t t t c − k c-k ck c c c 同余,所以再走 t t t 步就能到目标位置。

而现在 2 ∼ 9 2 \sim 9 29 t t t 步后也能到目标位置。

所以我们让所有人继续走,那么一定会相遇。

一共花费步数为 3 t + 2 k < 3 t + 3 c 3t+2k<3t+3c 3t+2k<3t+3c

#include <bits/stdc++.h>

using namespace std;

char ch[20];

int read()
{
	fflush(stdout);
	int x;
	scanf("%d", &x);
	for (int i = 1; i <= x; ++i)
		scanf("%s", ch);
	return x;
}

signed main()
{
	while (1)
	{
		printf("next 0\n");
		read();
		printf("next 0 1\n");
		int tot = read();
		if (tot == 2)
			break;
	}
	while (1)
	{
		printf("next 0 1 2 3 4 5 6 7 8 9\n");
		fflush(stdout);
		if (read() == 1)
			break;
	}
	printf("done");
	fflush(stdout);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值