CF EDR118 题解

A - Long Comparison

先比较位数,如果不相等直接输出。否则可以令 \(p_1, p_2\) 同时减去 \(\min(p_1, p_2)\),然后暴力把零加到 int 后面比较。显然有一个 \(p_i\) 等于 0 了,于是可以证明加完之后的 int 依然不超过 1e6,所以不会爆 int

code
void mian() {
	int x1 = read(), p1 = read(), x2 = read(), p2 = read(), l1 = 0, l2 = 0, c1 = x1, c2 = x2;
	while(c1) ++l1, c1 /= 10; while(c2) ++l2, c2 /= 10;
	if(l1 + p1 != l2 + p2) return puts(l1 + p1 < l2 + p2 ? "<" : ">"), void();
	int mn = min(p1, p2); p1 -= mn, p2 -= mn;
	x1 *= qpow(10, p1, 1e9), x2 *= qpow(10, p2, 1e9);
	puts(x1 == x2 ? "=" : x1 < x2 ? "<" : ">");
}

B - Absent Remainder

超级奇怪的题。每个数可以重复用的话那不就简单了,直接让最小的跟其它随便 \(\left\lfloor\dfrac n2\right\rfloor\) 个匹配就行了,mod 值一定小于最小数,肯定不属于 \(a\)

code
constexpr int N = 2e5 + 10;

int n, a[N];

void mian() {
	n = read();
	REP(i, 1, n) a[i] = read();
	sort(a + 1, a + n + 1);
	REP(i, 2, n / 2 + 1) printf("%d %d\n", a[i], a[1]);
}

C - Poisoned Dagger

巨大一眼的二分答案。里面直接模拟。复杂度 \(\mathrm O(Tn\log H)\)

code
constexpr int N = 110;

int n, H;
int a[N];

bool chk(int k) {
	int ans = 0;
	REP(i, 1, n) {
		if(i < n && a[i] + k >= a[i + 1]) ans += a[i + 1] - a[i];
		else ans += k;
	}
	return ans >= H;
}

void mian() {
	n = read(), H = read();
	REP(i, 1, n) a[i] = read();
	int ans = H;
	PER(i, 60, 0) if(ans - (1ll << i) >= 0 && chk(ans - (1ll << i))) ans -= 1ll << i;
	prt(ans), pc('\n');
}

D - MEX Sequences

挺有意思的一个题。

首先寻找 mex-correct 的序列的性质。我是以打表作为启发,当然可以不用。考虑动态在序列末尾加数,保持 mex-correct 的过程。设最后一个数事 \(x\),当当前有且仅有 \(0\sim x\) 这些数的时候,显然只有 \(x, x + 1, x + 2\) 可加。

  1. 如果加了 \(x\),情况没有丝毫变化。
  2. 如果加了 \(x + 1\),依然保持 \(0\) 到最后一个数都存在的性质。
  3. 如果加了 \(x + 2\),那么可以发现以后只能加 \(x\) 或者 \(x + 2\) 了……并且可以随便加。

于是一个 mex-correct 可以分成两部分,前面一部分事 111111....11112222....222223333.....33334.......4444444.............xxxx...xxxx,后面一部分是任意多的 \(x\)\(x + 2\) 按任意顺序排列(但我们限定后面一部分的第一个数必须是 \(x + 2\))。

我们考虑枚举该 mex-correct 的第一个 \(x + 2\) 在原序列是什么位置,统计答案。前后两部分显然是独立的,后面就对每个数记录一个桶 lower_bound 然后快速幂即可,重点在对前面计数。考虑一个 DP:\(dp_{i, j}\) 表示考虑到第 \(i\) 位,以数 \(j\) 结尾的第一部分数量。那么 \(i\to i + 1\) 的过程显然只有 \(dp_{a_i}\) 变化了,所以可以动态地一路维护 DP 数组,然后访问即可。总复杂度 \(\mathrm O(n\log n)\)

code
constexpr int N = 5e5 + 10;

int n;
int a[N];
vi pos[N];
int larger(int x, int p) {
	auto fd = upper_bound(pos[x].begin(), pos[x].end(), p);
	return pos[x].end() - fd;
}

int dp[N];

void mian() {
	n = read();
	REP(i, 0, n) pos[i].clear(), dp[i] = 0;
	REP(i, 1, n) a[i] = read(), pos[a[i]].pb(i);
	int ans = 0;
	REP(i, 1, n) {
		addto(dp[a[i]], add(a[i] ? dp[a[i] - 1] : 1, dp[a[i]]));
		int add = a[i] == 0 ? 0 : a[i] == 1 ? 1 : dp[a[i] - 2];
		int mul = qpow(2, larger(a[i], i) + (a[i] >= 2 ? larger(a[i] - 2, i) : 0));
		addto(ans, (ll)add * mul % P);
//		cout << ans << "?\n";
	}
	REP(i, 0, n) addto(ans, dp[i]);
	prt(ans), pc('\n');
}

E - Crazy Robot

想要 AC 并不难,但是要理解本质的话还是有点意思的。

显然,对于那些加号的位置,当走到它上面的时候,决策(堵哪个方向)必然是固定的。若格子 \(A\) 按照决策走一步可能走到格子 \(B\),则称 \(A\) 依赖于 \(B\),那么依赖关系必然不可能有环(那样就有无限循环的可能,这些就不可能是加号),所以这些加号点的转移关系必然是有拓扑序的,只不过我们不知道它的拓扑序。

这种看似有环的转移,而保留真正有用的转移之后却是无环,但我们不知道具体的拓扑序,只能动态决定。仿照 dijkstra 的思想,每次在已经决定的基础上,跟据具体题意再决定一个(就是拓扑序的下一个),直到决定完为止。

对于这题,首先能决定的是 lab 的位置。然后每次,基于所有已经决定能到达 lab 的格子(称为 rab),那些周围至少有一个 rab、并且周围不是 rab 或者障碍的位置不超过 1 个的地方也可以被决定,于是类似 bfs 用个队列维护,每次加入一个点就更新周围四个点,即可,复杂度 \(\mathrm O(16nm)\)。二维数组要用 vector<vector> 存,傻逼(怎么说话呢,骂两个老师高兴啊?)。

code
constexpr int N = 1e6 + 10;

int n, m;
vector<vector<char>> a;
int sx, sy;

tii q[N]; int head, tail;
vector<vector<bool>> vis;
constexpr int dx[] = {0, 1, 0, -1}, dy[] = {-1, 0, 1, 0};
bool valid(int x, int y) { return 1 <= x && x <= n && 1 <= y && y <= m; }
bool chk(int x, int y) {
	if(!valid(x, y)) return false;
	if(vis[x][y] || a[x][y] == '#') return false;
	int cnt1 = 0, cntcan = 0;
	REP(i, 0, 3) {
		int xn = x + dx[i], yn = y + dy[i];
		if(!valid(xn, yn)) continue;
		if(vis[xn][yn]) ++cnt1;
		if(!vis[xn][yn] && a[xn][yn] == '.') ++cntcan;
	}
	return cntcan <= 1 && cnt1;
}
void bfs() {
	head = tail = 0;
	vis = vector<vector<bool>>(n + 1, vector<bool>(m + 1, false));
	q[tail++] = mt(sx, sy), vis[sx][sy] = true;
	while(head < tail) {
		int x, y; tie(x, y) = q[--tail];
		REP(i, 0, 3) {
			int xn = x + dx[i], yn = y + dy[i];
			if(chk(xn, yn)) vis[xn][yn] = true, q[tail++] = mt(xn, yn);
		}
	}
}

void mian() {
	n = read(), m = read();
	a = vector<vector<char>>(n + 1, vector<char>(m + 1));
	REP(i, 1, n) {
		static char tmp[N];
		scanf("%s", tmp + 1);
		REP(j, 1, m) a[i][j] = tmp[j];
	}
	REP(i, 1, n) REP(j, 1, m) if(a[i][j] == 'L') sx = i, sy = j;
	bfs();
	REP(i, 1, n) REP(j, 1, m) if(mt(i, j) != mt(sx, sy) && vis[i][j]) a[i][j] = '+';
	REP(i, 1, n) { REP(j, 1, m) pc(a[i][j]); pc('\n'); }
}

F - Tree Coloring

CF1613F - Tree Coloring 题解 - ycx060617 - 博客园 (cnblogs.com)

posted @ 2021-12-06 16:51  ycx060617  阅读(160)  评论(0编辑  收藏  举报