「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 st,uv的最短路的交集一定是连续的。

所以隐式建出 s − t s-t st的最短路DAG,选出一条 s − t s-t st的最短路上的两个点 x , y x,y x,y,使得 d i s ( u , x ) + d i s ( y , v ) dis(u,x)+dis(y,v) dis(u,x)+dis(y,v)最小。

答案就是上式与 d i s ( u , v ) dis(u,v) dis(u,v) m i n min 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 6

那么如果'0''1'出现次数最少,那么可以用高维前缀和进行容斥。否则,直接枚举'?'代表的数统计答案即可。

时间复杂度: O ( 2 n n + 2 6 n ) O(2^nn+2^6n) O(2nn+26n)

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

const int maxn = 21;

char s[1 << maxn];
int n, L, q, f[1 << maxn], g[1 << maxn], w[1 << maxn], bcnt[1 << 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(); L = 1 << n; q = gi();

    scanf("%s", s);
    for (int i = 0; i < L; ++i) w[i] = f[i] = g[i] = s[i] - '0', bcnt[i] = bcnt[i >> 1] + (i & 1);

    for (int i = 1; i < L; i <<= 1)
        for (int j = 0; j < L; ++j)
            if (j & i) f[j] += f[i ^ j], g[i ^ j] += g[j];

    int p0, p1, p2, ans;
    while (q--) {
        scanf("%s", s);
        p0 = 0; p1 = 0; p2 = 0; ans = 0;
        for (int i = 0; i < n; ++i)
            if (s[n - i - 1] == '0') p0 |= 1 << i;
            else if (s[n - i - 1] == '1') p1 |= 1 << i;
            else p2 |= 1 << i;
        if (bcnt[p0] <= 6) {
            for (int s = p0; ; s = (s - 1) & p0) {
                if (bcnt[s] & 1) ans -= g[s | p1];
                else ans += g[s | p1];
                if (!s) break;
            }
        } else if (bcnt[p1] <= 6) {
            for (int s = p1; ; s = (s - 1) & p1) {
                if (bcnt[s ^ p1] & 1) ans -= f[s | p2];
                else ans += f[s | p2];
                if (!s) break;
            }
        } else {
            for (int s = p2; ; s = (s - 1) & p2) {
                ans += w[p1 | s];
                if (!s) break;
            }
        }
        printf("%d\n", ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值