Bootstrap

【题解】Codeforces Round 996 C.The Trail D.Scarecrow

Codeforces Round 996

比赛地址:https://codeforces.com/contest/2055

Problem C.The Trail

1.从数学上看,未知的数有 n+m-1 个位置的 a[i] 值,和行列总和x,解出他们需要n + m个独立的方程。对每一个未知的位置,有 行和 等于 列和 的方程,共n+m-1个,还有一个行和/列和 = x的方程,恰好可解。所以只需要找到一种易于用代码表达的解方程方法即可。

2.从(1,1)开始,按字符串的顺序依次将每个空余位置表示成 ax + b 的形式。对于每行的最后一个未知数,此行仅剩该数未被表示,用 x - 行和(不含该数) 表示该数。对于每行的其他未知数,此列仅剩该数位被表示,用 x - 列和(不含该数)表示。写代码时用矩阵a[i][j]、b[i][j]表示i,j位置的a、b值,分别处理即可,详见AC代码。

3.现在所有未知数均被表示成ax+b的形式,如果最后一个未知数(n,m),由 x-行和得到,那用第m列的列和 = x解出x;反正,由 x-列和得到,就用行和 = x解方程。写代码时注意x系数为0的情况,不能除以0,此时特判。

#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int N = 2e3 + 10, M = 20;

string s;
int n, m;
int a[N][N];
int b[N][N];

void solve() {
	cin >> n >> m >> s;
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++) {
			cin >> a[i][j];
			b[i][j] = 0;
		}
	int x = 1, y = 1;
	for (auto i : s) {
		if (i == 'D') {
			int sumb = 0, suma = 0;
			for (int j = 1; j <= m; j ++) {
				sumb += b[x][j];
				suma += a[x][j];
			}
			b[x][y] = 1 - sumb;
			a[x][y] = 0 - suma;
			x ++;
		} else {
			int sumb = 0, suma = 0;
			for (int j = 1; j <= n; j ++) {
				sumb += b[j][y];
				suma += a[j][y];
			}
			b[x][y] = 1 - sumb;
			a[x][y] = 0 - suma;
			y ++;
		}
	}
	long long sum;
	if (s.back() == 'D') {
		{
			int sumb = 0, suma = 0;
			for (int j = 1; j <= n; j ++) {
				sumb += b[j][y];
				suma += a[j][y];
			}
			b[x][y] = 1 - sumb;
			a[x][y] = 0 - suma;
		}
		int sumb = 0, suma = 0;
		for (int i = 1; i <= m; i ++) {
			suma += a[n][i];
			sumb += b[n][i];
		}
		if (sumb == 1)
			sum = 0;
		else
			sum = suma / (1 - sumb);
	} else {
		{
			int sumb = 0, suma = 0;
			for (int j = 1; j <= m; j ++) {
				sumb += b[x][j];
				suma += a[x][j];
			}
			b[x][y] = 1 - sumb;
			a[x][y] = 0 - suma;
		}
		int sumb = 0, suma = 0;
		for (int i = 1; i <= n; i ++) {
			suma += a[i][m];
			sumb += b[i][m];
		}
		if (sumb == 1)
			sum = 0;
		else
			sum = suma / (1 - sumb);
	}
	for (int i = 1; i <= n; i ++) {
		for (int j = 1; j <= m; j ++) {
			cout << sum *b[i][j] + a[i][j] << ' ';
		}
		cout << '\n';
	}
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;

	while (t --) {
		solve();
	}
	return 0;
}

Problem D.Scarecrow

1.赶鸟的第一步,肯定是让最近的稻草人走到0,把鸟赶出来; 赶鸟的最后一步,是最远的稻草人把鸟一直赶到终点(当l较大,ai较小时)。
2.所有稻草人速度一样,所以稻草人不可能超过彼此。
3.每次鸟跳跃之后,只需要考虑离瞬移之后的鸟之前和鸟之后最近的两个稻草人,分别成为前稻草人和后稻草人。
4.考虑维护每个稻草人的活动范围,在时刻t,第i个稻草人可以在[ai - t,ai + t]的范围。首先让最近的稻草人把鸟赶出来,花费时间a1,然后判断鸟和前后两个稻草人的位置关系。位置关系分为4种:
a.鸟 < ai-t;
b. ai-t<=鸟<=ai+t;
c. ai + t< 鸟 < ai+t+k;
d. 鸟 > ai + t + k;
对于情况a,由前稻草人推,后稻草人往回赶,触发瞬移之后鸟到后稻草人+k的位置,耗时距离差/2,更新该机器人为前稻草人;
对于情况b, 后稻草人在鸟处等着,鸟瞬移过来直接顶走,耗时0,更新该机器人为前稻草人;
对于情况c,后稻草人值走到尽可能大的位置,鸟瞬移过来直接顶走,耗时0,更新该机器人为前稻草人;
对于情况d,此处不操作,该机器人不可能作为后稻草人,更新该机器人为前稻草人,看下一个稻草人;
5.当稻草人使用完,还没有到达l处,最后一个稻草人推过去即可。

#include <bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int N = 2e5 + 10;

int n, k, l;
int a[N];


void solve() {
	cin >> n >> k >> l;
	k = k << 1;
	l = l << 1;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
		a[i] = a[i] << 1;
	}
	int ans = 0;
	ans += a[1];
	int idx = k;
	int p = 2;
	int last = 0;
	while (idx < l) {
		if (p == n + 1) {
			ans += l - k - last;
			break;
		}
		if (idx < a[p] - ans) {
			int now = a[p] - ans;
			int t = (now - last - k) >> 1;
			idx = now - t + k;
			ans += t;
			last = idx - k;
		} else if (idx >= a[p] - ans && idx <= a[p] + ans) {
			last = idx;
			idx += k;
			ans += 0;
		} else if (idx - (a[p] + ans) < k) {
			idx = k + a[p] + ans;
			last = a[p] + ans;
			ans += 0;
		} else {
			last = max(last, a[p] + ans);
		}
		p ++;
	}
	cout << ans << '\n';
}

signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t = 1;
	cin >> t;
	while (t --) {
		solve();
	}
	return 0;
}

Problem E.Haystacks

** 注:该题难度高达 2800,作者也没能独立完成QAQ**

;