AtCoder Grand Contest 036 简要题解

从这里开始

Problem A Triangle

  考虑把三角形移到和坐标轴相交,即

 

 

   然后能够用坐标比较简单地计算面积,简单构造一下就行了。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool bolean;

#define ll long long

const int V = 1e9;

ll S;

int main() {
	scanf("%lld", &S);
	if (S == 1000000000000000000ll) {
		printf("%d 0 0 0 0 %d\n", V, V);
		return 0;
	}
	int z = S / V;
	int y = V;
	int x = 1;
	int h = S % V;
	printf("0 %d %d 0 %d %d\n", y, x, x + z, h);
	return 0;
}

Problem B Do Not Duplicate

  你发现 $x$ 如果还会再出现,会把之间的数都删掉,所以 $x$ 向它的后继连一条边。然后模拟一下就行了。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

const int N = 2e5 + 5;

int n;
ll K;
int a[N], h[N];
int fa[N], c[N];
boolean vis[N];

int main() {
	scanf("%d%lld", &n, &K);
	for (int i = 0; i < n; i++) {
		scanf("%d", a + i);
	}
	for (int i = n - 1; ~i; i--)
		h[a[i]] = i + n;
	for (int i = n - 1; ~i; i--) {
		int suf = h[a[i]];
		fa[i] = (suf + 1) % n;
		c[i] = (suf + 1) / n;
		h[a[i]] = i;
	}
	int p = 0, q;
	vector<int> stk;
	do {
		vis[p] = true;
		stk.push_back(p);
		p = fa[p];
	} while (!vis[p]);
	int sumcost = 0;
	do {
		q = stk.back();
		stk.pop_back();
		sumcost += c[q];
	} while (q ^ p);
	stk.clear();
	int x = 0;
	while (K - c[x] >= 1 && (x ^ p))
		K -= c[x], x = fa[x];
	if (x == p)
		K = (K - 1) % sumcost + 1;
	while (K - c[x] >= 1)
		K -= c[x], x = fa[x];
	memset(vis, false, sizeof(vis));
	for (int i = x; i < n + (K - 1) * n; i++) {
		int z = a[i % n];
		if (!vis[z]) {
			stk.push_back(z);
			vis[z] = true;
		} else {
			int y = 0;
			do {
				y = stk.back();
				stk.pop_back();
				vis[y] = false;
			} while (y ^ z);
		}
	}
	for (auto x : stk) {
		printf("%d ", x);
	}
	return 0;
}

Problem C GP 2

  考虑一些必要条件:

  • 和为 $3M$
  • 最大值不超过 $2M$
  • 奇数个数不超过 $M$

  可以对最大值和最大奇数或剩下的最大值进行讨论,然后用归纳法证明。

  剩下是基础计数。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
	if (!b) {
		x = 1, y = 0;
	} else {
		exgcd(b, a % b, y, x);
		y -= (a / b) * x;
	}
}

int inv(int a, int n) {
	int x, y;
	exgcd(a, n, x, y);
	return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
	public:
		int v;

		Z() : v(0) {	}
		Z(int x) : v(x){	}
		Z(ll x) : v(x % Mod) {	}

		friend Z operator + (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
		}
		friend Z operator - (const Z& a, const Z& b) {
			int x;
			return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
		}
		friend Z operator * (const Z& a, const Z& b) {
			return Z(a.v * 1ll * b.v);
		}
		friend Z operator ~(const Z& a) {
			return inv(a.v, Mod);
		}
		friend Z operator - (const Z& a) {
			return Z(0) - a;
		}
		Z& operator += (Z b) {
			return *this = *this + b;
		}
		Z& operator -= (Z b) {
			return *this = *this - b;
		}
		Z& operator *= (Z b) {
			return *this = *this * b;
		}
		friend boolean operator == (const Z& a, const Z& b) {
			return a.v == b.v;
		} 
};

Z<> qpow(Z<> a, int p) {
	Z<> rt = Z<>(1), pa = a;
	for ( ; p; p >>= 1, pa = pa * pa) {
		if (p & 1) {
			rt = rt * pa;
		}
	}
	return rt;
}

typedef Z<> Zi;

const int N = 3e6 + 5;

int n, m;
Zi fac[N], _fac[N];

void prepare(int n) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = fac[i - 1] * i;
	_fac[n] = ~fac[n];
	for (int i = n; i; i--)
		_fac[i - 1] = _fac[i] * i;
}
Zi comb(int n, int m) {
	return (n < m) ? (0) : (fac[n] * _fac[m] * _fac[n - m]);
}

int main() {
	scanf("%d%d", &n, &m);
	prepare(3 * m + n - 1);
	Zi ans = comb(3 * m + n - 1, n - 1) - comb(m + n - 2, n - 1) * n;
	for (int num = m + 1; num <= n && num <= 3 * m; num++) {
		if ((3 * m - num) & 1)
			continue;
		int sum = (3 * m - num) >> 1;
		Zi tmp = comb(sum + n - 1, n - 1);
		// mx is odd
		tmp -= num * comb(sum - m + n - 1, n - 1);
		// mx is even
		tmp -= (n - num) * comb(sum - m + n - 2, n - 1);
		tmp *= comb(n, num);
		ans -= tmp;
	}
	printf("%d\n", ans.v);
	return 0;
}

Problem D Negative Cycle

  设到第 $i$ 个点的最短路为 $d_i$。不难发现 $d_i \geqslant d_{i + 1}$,如果存在负环,那么会更新。

  考虑最短路等于 $d$ 和最短路等于 $d + 1$ 的两段 $[a, b]$,以及 $(b, c]$。

  那么 $(b, c]$ 不能有到 $a$ 之前的边,否则 $d$ 会被更新。$(b + 1, c]$ 之间不能有从前连向后面的边,否则 $d$ 也会被更新。

  状态中记入 $a, b$,稍作优化就能做到 $O(n^3)$。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define ll long long

const ll llf = (signed ll) (~0ull >> 2);

const int N = 505;

int n;
int A[N][N];
ll s[N][N];
ll dp[N][N];

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			if (i == j) {
				continue;
			}
			scanf("%d", A[i] + j);
			s[i][j] = A[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
		}
	}
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			dp[i][j] = llf;
	dp[1][1] = 0;
	for (int i = 2; i <= n; i++) {
		dp[1][i] = dp[1][i - 1];
		for (int j = 1; j < i; j++) {
			dp[1][i] += A[j][i];
		}
	}
	for (int i = 2; i <= n; i++) {
		ll cost = 0;
		for (int b = i - 1; b; b--) {
			for (int j = b + 2; j <= i; j++)
				cost += A[b + 1][j];
			for (int a = b; a; a--) {
				dp[b + 1][i] = min(dp[b + 1][i], dp[a][b] + cost + s[i][a - 1] - s[b][a - 1]);
			}
		}
	}
	ll ans = llf;
	for (int i = 1; i <= n; i++)
		ans = min(ans, dp[i][n]);
	printf("%lld\n", ans);	
	return 0;
}

Problem E ABC String

  先将连续的字符缩起来,设出现的次数满足 $a \leqslant b \leqslant c$。

  现在我们希望删掉一些 $a$ 使得存在一种方案使得删掉一些 $b, c$ 使得 $a = b = c$。

  不难发现,删掉一个 A 后至多使 $b$ 或 $c$ 减少 1。因此 A 仍然是出现次数最少的字符。

  假设删除一些 A 之后,仍然满足 $b \leqslant c$。

  如果 $b = c$,那么每次删掉 BC 或者 CB 就行了。

  考虑 $b - c$ 所能到达的最小值,如果一段中含 B,那么可以贡献 -1,如果只有一个 C,那么会贡献 1。

  因此,我们希望删掉一些 A,使得 $b - c$ 的最小值能小于等于 0。

  不难发现,只用删掉若干个单独的 C 之前的 A,并且删掉一个 A 至多使单独的 C 减少 1 个。

Code

#include<bits/stdc++.h>
using namespace std;
typedef bool boolean;

const int N = 1e6 + 5;

int n;
char s[N], s1[N];

void shrink() {
	int t = 0;
	for (int i = 1; i <= n; i++) {
		if (s[i] ^ s[i - 1]) {
			s[++t] = s[i];
		}
	}
	n = t;
}

int cnt[3];
char pg[3], qg[3];
void trans() {
	for (int i = 1; i <= n; i++) {
		cnt[s[i] - 'A']++;
	}
	vector<pair<int, int>> a;
	a.emplace_back(cnt[0], 0);
	a.emplace_back(cnt[1], 1);
	a.emplace_back(cnt[2], 2);
	sort(a.begin(), a.end());
#define make_trans(x, y) pg[x] = y, qg[y] = x;
	for (int i = 0; i < 3; i++)
		make_trans(a[i].second, i);
	for (int i = 1; i <= n; i++) {
		s[i] = pg[s[i] - 'A'] + 'A';
	}
}

void make_valid() {
	int x = 0, y = 0;
	auto count = [&] (int l, int r) -> boolean {
		if (l > r) return false;
		for (int i = l; i <= r; i++) {
			if (s[i] == 'B') {
				x++;
				return false;
			}
		}
		if (l == 1 || r == n)
			return false;
		y++;
		return true;
	};
	int ls = 0;
	vector<int> pos;
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'A') {
			if (count(ls + 1, i - 1) && ls) {
				pos.push_back(i);
			}
			ls = i;
		}
	}
	count(ls + 1, n);
	if (x < y) {
		int t = 0;
		pos.resize(y - x);
		reverse(pos.begin(), pos.end());
		for (int i = 1; i <= n; i++) {
			if (!pos.empty() && i == pos.back()) {
				pos.pop_back();
			} else {
				s[++t] = s[i];
			}
		}
		n = t;
		shrink();
		s[n + 1] = 0;
	}
	memset(cnt, 0, sizeof(cnt));
	for (int i = 1; i <= n; i++)
		cnt[s[i] - 'A']++;
	int d = cnt[2] - cnt[1];
	int t = 0;
	char target = 'C';
	(d < 0) && (d = -d, target = 'B');
	ls = 0;
	auto remove = [&] (int l, int r) {
		if (l > r)
			return;
		if ((l == 1 || r == n) || (l ^ r)) {
			if (s[l] == target && d) l++, d--;
			if (l < r && s[r] == target && d) r--, d--;
		}
		for (int i = l; i <= r; i++)
			s1[++t] = s[i];
	};
	for (int i = 1; i <= n; i++) {
		if (s[i] == 'A') {
			remove(ls + 1, i - 1);
			s1[++t] = 'A';
			ls = i;
		}
	}
	remove(ls + 1, n);
	n = t;
	for (int i = 1; i <= n; i++) {
		s[i] = s1[i];
	}
	s[n + 1] = 0;
}

void make_equal() {
	memset(cnt, 0, sizeof(cnt));
	for (int i = 1; i <= n; i++)
		cnt[s[i] - 'A']++;
	assert(cnt[1] == cnt[2]);
	int d = cnt[1] - cnt[0];
	int t = 0;
	vector<pair<int, int>> pos;
	for (int i = 1; i <= n; i++) {
		if (i < n && d && s[i] != 'A' && s[i + 1] != 'A') {
			if (s1[t] == 'A' && s[i + 2] == 'A') {
				pos.emplace_back(t + 1, t + 2);
				s1[++t] = s[i];
			} else {
				i++, d--;
			}
		} else {
			s1[++t] = s[i];
		}
	}
	n = t;
	for (int i = 1; i <= n; i++)
		s[i] = s1[i];
	vector<int> delpos;
	for (int i = 0; i < d; i++) {
		delpos.push_back(pos[2 * i].first);
		delpos.push_back(pos[2 * i + 1].second);
	}
	t = 0;
	reverse(delpos.begin(), delpos.end());
	for (int i = 1; i <= n; i++) {
		if (!delpos.empty() && delpos.back() == i) {
			delpos.pop_back();
		} else {
			s[++t] = s[i];
		}
	}
	t = n;
	assert(delpos.empty());
}

int main() {
	scanf("%s", s + 1);
	n = strlen(s + 1);
	shrink();
	trans();
	make_valid();
	make_equal();
	for (int i = 1; i <= n; i++)
		s[i] = qg[s[i] - 'A'] + 'A';
	s[n + 1] = 0;
	puts(s + 1);
	return 0;
}

Problem F Square Constraints

  考虑每个位置有一个上界 $R_i$ 和一个下界 $L_i$。对于后 $N$ 个位置下界为 0。

  考虑只有上界,我们把上界从小到大排序,那么答案等于 $\prod_{i = 0}^{2N - 1} (R_i - i)$。

  考虑对一些下界进行容斥。不难注意到下界非 0 的位置,它的上界是排在后 $N$ 个位置的上界之后。

  所以只用枚举一下有多少个数被硬点不合法就可以 dp 了。

Code

#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

#define pii pair<int, int>
#define ll long long

const int N = 505;

int n, n2, Mod;
pii a[N];
int cnt[N];
int dp[2][N];

int main() {
	scanf("%d%d", &n, &Mod);
	n2 = n << 1;
	int p1 = 0, p2 = 0, N4 = 4 * n * n, N2 = n * n;
	for (int i = n2 - 1; ~i; i--) {
		while (p1 < n2 && p1 * p1 <= N4 - i * i)
			p1++;
		while (p2 * p2 < N2 - i * i)
			p2++;
		if (p2) {
			a[i] = pii(p2, p1);
		} else {
			a[i] = pii(p1, 0);
		}
	}
	sort(a, a + n2);
	cnt[0] = 0;
	for (int i = 0; i < n2; i++)
		cnt[i + 1] = cnt[i] + !a[i].second;
	ll ans = 0;
	for (int i = 0; i <= n; i++) {
		int cur = 0;
		memset(dp[0], 0, sizeof(dp[0]));
		dp[0][0] = 1;
		for (int j = 0; j < n2; j++) {
			memset(dp[cur ^= 1], 0, sizeof(dp[0]));
			for (int k = 0; k <= i; k++) {
				if (!a[j].second) {
					dp[cur][k] = (dp[cur][k] + 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod;
				} else {
					dp[cur][k + 1] = (dp[cur][k + 1] - 1ll * (a[j].first - k - cnt[j]) * dp[cur ^ 1][k]) % Mod;
					dp[cur][k] = (dp[cur][k] + 1ll * (a[j].second - (n + i + j - cnt[j] - k)) * dp[cur ^ 1][k]) % Mod;
				}
			}
		}
		ans += dp[cur][i];
	}
	ans %= Mod;
	(ans < 0) && (ans += Mod);
	printf("%d\n", (int) ans);
	return 0;
}

 

posted @ 2019-10-20 22:30  阿波罗2003  阅读(317)  评论(0编辑  收藏  举报