1. 程式人生 > >「JOI 2018 Final」簡要題解

「JOI 2018 Final」簡要題解

題目是LOJ2347-LOJ2351

「JOI 2018 Final」寒冬暖爐

貪心小水題。選最大的間隔即可。

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

const int maxn = 100005;

int n, k, T[maxn], ans, a[maxn];

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c &&
c <= '9') sum = sum * 10 + c - 48, c = getchar(); return sum; } int main() { n = gi(); k = gi(); --k; for (int i = 1; i <= n; ++i) T[i] = gi(); for (int i = 2; i <= n; ++i) a[i] = T[i] - (T[i - 1] + 1); ans = T[n] - T[1] + 1; sort(a + 1, a + n + 1, greater<int>()); for (int i =
1; i <= k; ++i) ans -= a[i]; printf("%d\n", ans); return 0; }

「JOI 2018 Final」美術展覽

貪心小水題。用字首和轉化式子然後選最大最小即可。

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

typedef long long lint;
const int maxn = 500005;

int n;
lint Max[maxn], sum[maxn], ans;

struct node
{
	lint A, B;

	bool operator
< (const node &x) { return A < x.A; } } p[maxn]; inline lint gi() { char c = getchar(); while (c < '0' || c > '9') c = getchar(); lint sum = 0; while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar(); return sum; } int main() { n = gi(); for (int i = 1; i <= n; ++i) p[i].A = gi(), p[i].B = gi(); sort(p + 1, p + n + 1); Max[0] = -(1ll << 60); for (int i = 1; i <= n; ++i) { sum[i] = sum[i - 1] + p[i].B; Max[i] = max(Max[i - 1], p[i].A - sum[i - 1]); } lint Min = 1ll << 60; for (int i = n; i >= 1; --i) { if (Min > p[i].A - sum[i]) Min = p[i].A - sum[i]; ans = max(ans, Max[i] - Min); } printf("%lld\n", ans); return 0; }

「JOI 2018 Final」糰子製作

非常有意思的一道DP。考慮不在同一對角線的糰子串不會互相影響,所以在對角線上進行DP。狀態為橫放/豎放/不放。

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

const int maxn = 3005;

int n, m;
char s[maxn][maxn];

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

inline int check(int x, int y)
{
	return (s[x - 1][y] == 'R' && s[x][y] == 'G' && s[x + 1][y] == 'W') << 1 |
    	   (s[x][y - 1] == 'R' && s[x][y] == 'G' && s[x][y + 1] == 'W');
}

int main()
{
	n = gi(); m = gi();
	for (int i = 1; i <= n; ++i) scanf("%s", s[i] + 1);

	register int x, y, t, A, B, C, g, ans = 0;
	for (int i = 1; i <= n + m - 1; ++i) {
		x = min(i, n), y = max(1, i - n + 1), A = 0, B = 0, C = 0;
		while (x && y <= m) {
			t = check(x, y), g = C;
			C = max(C, max(A, B));
			if (t & 1) A = max(A, g) + 1;
			if (t & 2) B = max(B, g) + 1;
			--x; ++y;
		}
		ans += max(A, max(B, C));
	}

	printf("%d\n", ans);
	
	return 0;
}

「JOI 2018 Final」月票購買

考慮一定存在一種方案,使得 s t , u v s-t,u-v 的最短路的交集一定是連續的。

所以隱式建出 s t s-t 的最短路DAG,選出一條 s t s-t 的最短路上的兩個點 x , y x,y ,使得 d i s ( u , x ) + d i s ( y , v ) dis(u,x)+dis(y,v) 最小。

答案就是上式與 d i s ( u , v ) dis(u,v) m i n min

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

typedef long long lint;
const int maxn = 100005;

int n, m, s, t, u, v;

struct edge
{
	int to, next, w;
} e[maxn * 4];
int h[maxn], tot;

lint dis_s[maxn], dis_t[maxn], dis_u[maxn], dis_v[maxn];
bool vis[maxn];
priority_queue<pair<lint, int>, vector<pair<lint, int> >, greater<pair<lint, int> > > que;

inline int gi()
{
	char c = getchar();
	while (c < '0' || c > '9') c = getchar();
	int sum = 0;
	while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
	return sum;
}

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

void dijkstra(int s, lint *dis)
{
	memset(dis + 1, 63, sizeof(lint) * n);
	memset(vis + 1, 0, sizeof(bool) * n);
	dis[s] = 0;
	que.push(make_pair(dis[s], s));

	int u;
	while (!que.empty()) {
		u = que.top().second; que.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
			if (dis[v] > dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				que.push(make_pair(dis[v], v));
			}
	}
}

lint Min_u[maxn], Min_v[maxn], ans = 1ll << 60;
void dfs(int x)
{
	if (vis[x] == 1) return ;
	vis[x] = 1;
	Min_u[x] = dis_u[x];
	Min_v[x] = dis_v[x];
	for (int i = h[x], y; y = e[i].to, i; i = e[i].next)
		if (dis_s[x] + dis_t[y] + e[i].w == dis_s[t]) {
			dfs(y);
			if (Min_u[x] + Min_v[x] > min(dis_u[x], Min_u[y]) + min(dis_v[x], Min_v[y])) {
				Min_u[x] = min(dis_u[x], Min_u[y]);
				Min_v[x] = min(dis_v[x], Min_v[y]);
			}
		}
	ans = min(ans, dis_v[x]);
}

int main()
{
	n = gi(); m = gi();
	s = gi(); t = gi(); u = gi(); v = gi();

	for (int u, v, i = 1; i <= m; ++i) u = gi(), v = gi(), add(u, v, gi());

	dijkstra(s, dis_s);
	dijkstra(t, dis_t);
	dijkstra(u, dis_u);
	dijkstra(v, dis_v);

	memset(vis + 1, 0, sizeof(bool) * n);
	dfs(s);

	printf("%lld\n", min(Min_u[s] + Min_v[s], dis_u[v]));
	
	return 0;
}

「JOI 2018 Final」毒蛇越獄

比較巧妙的題目。

可以發現,詢問串中的'0','1','?'出現次數最少的字元的出現次數不會超過 6 6

那麼如果'0''1'出現次數最少,那麼可以用高維字首和進行容斥。否則,直接列舉'?'代表的數統計答案即可。

時間複雜度: O ( 2 n n + 2 6 n ) O(2^nn+2^6n)