HHKB Programming Contest 2025(AtCoder Beginner Contest 388)

A - ?UPC

题意:给你一个字符串,把他的第一个字符和"UPC"输出。

输出即可。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    std::cout << s[0] << "UPC\n";
}

B - Heavy Snake

题意:n条蛇由厚度和长度,重量为厚度乘长度,问长度加上1~k时,最大的蛇的重量分别是多少。

直接模拟即可。

点击查看代码
void solve() {
    int n, d;
    std::cin >> n >> d;
    std::vector<int> a(n), b(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i] >> b[i];
    }

    for (int i = 1; i <= d; ++ i) {
    	int ans = 0;
    	for (int j = 1; j < n; ++ j) {
    		if (a[j] * (b[j] + i) > a[ans] * (b[ans] + i)) {
    			ans = j;
    		}
    	}
    	std::cout << a[ans] * (b[ans] + i) << "\n";
    }
}

C - Various Kagamimochi

题意:给你升序的n个数,一个数可以放在大于等于它的两倍的数上面,问有多少匹配的数。

从后往前双指针找可以匹配的最大的,那么前面的所有数都可以匹配。
也可以二分。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }
    i64 ans = 0;
    for (int i = n - 1, j = n - 1; i >= 0; -- i) {
    	while (j >= 0 && a[j] > a[i] / 2) {
    		-- j;
    	}

    	ans += j + 1;
    }

    std::cout << ans << "\n";
}

D - Coming of Age Celebration8

题意:n个人,每个人有一些石头,每个人第i年会向前面的人要一个石头(如果有),问最后每个人手上的石头数。

第i个人要加上前面的石头,然后给后面连续的min(n - i, a[i])个人一个石头,加石头可以差分做,循环过去判断就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<int> d(n + 2);
    for (int i = 1; i <= n; ++ i) {
    	d[i] += d[i - 1];
    	a[i] += d[i];
    	if (a[i]) {
    		d[i + 1] += 1;
    		d[std::min(n + 1, i + a[i] + 1)] -= 1;
    	}

    	a[i] = std::max(0, a[i] - (n - i));
    }

    for (int i = 1; i <= n; ++ i) {
    	std::cout << a[i] << " \n"[i == n];
    }
}

E - Simultaneous Kagamimochi

题意:给你升序的n个数,一个数可以放在大于等于它的两倍的数上面,问最多匹配多少个。

刚开始想着贪心匹配wa了两发,然后二分写挂一发。。。
我们肯定要选前面最小的几个和后面最大的匹配,二分这个匹配数,看对应的数能否匹配上就行。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    auto check = [&](int p) -> bool {
    	for (int i = n - 1; p >= 0 ; -- i, -- p) {
    		if (a[p] * 2 > a[i]) {
    			return false;
    		}
    	}

    	return true;
    };

    int l = 0, r = n / 2 - (n % 2 == 0);
    while (l < r) {
    	int mid = l + r + 1 >> 1;
    	if (check(mid)) {
    		l = mid;
    	} else {
    		r = mid - 1;
    	}
    }

    if (!check(l)) {
    	std::cout << 0 << "\n";
    } else {
    	std::cout << l + 1 << "\n";
    }
}

F - Dangerous Sugoroku

看了一会感觉会又不会,然后看g过的多就去看g了。

题意:你要从1跳到n,每次跳跃的距离是a到b之间的一个数,但你不能跳到给定的一些区间里。

这题和[NOIP2005]过河有一点像,所以我赛时有一种熟悉的感觉。
注意到给定的区间的长度和ab都很小,那么有很多点是没用的,如果一段区间长度大于b*(b-1),那么大于的那部分都可以走到,证明比较复杂,可以看看洛谷上面的题解https://www.luogu.com.cn/problem/solution/P1052

那么如果给一个较小的n,我们知道f[i] 可以转移到 f[i + a] ~ f[i + b]。所以我们把能走的区间缩成一个个不超过b*(b-1)的区间,那么可以进行dp,这个题缩区间后两个点之间的距离还要加上中间不能走的区间的总长度。dp转移可以双指针优化,因为后面的点可以转移到的点一定在前面点可以转移点的后面。
注意特判没有不合法区间和ab相等的情况。

点击查看代码
void solve() {
    i64 n, m, A, B;
    std::cin >> n >> m >> A >> B;
    std::vector<i64> l(m), r(m);
    for (int i = 0; i < m; ++ i) {
    	std::cin >> l[i] >> r[i];
    }

    if (A == B) {
    	if (A > 1 && n % A != 1) {
    		std::cout << "No\n";
    		return;
    	}

    	for (int i = 0; i < m; ++ i) {
    		for (i64 j = l[i]; j <= r[i]; ++ j) {
    			if (j % A == 1) {
    				std::cout << "No\n";
    				return;
    			}
    		}
    	}

    	std::cout << "Yes\n";
    	return;
    }

    if (m == 0) {
    	if (n >= B * B) {
    		std::cout << "Yes\n";
    	} else {
    		std::vector<int> f(n + 1);
    		f[1] = 1;
    		for (int i = 1; i <= n; ++ i) {
    			for (int j = i + A; j <= n && j <= i + B; ++ j) {
    				f[j] |= f[i];
    			}
    		}

    		if (f[n]) {
    			std::cout << "Yes\n";
    		} else {
    			std::cout << "No\n";
    		}
    	}
    	return;
    }

    std::vector<int> len(m + 1);
    for (int i = 0; i <= m; ++ i) {
    	if (i == 0) {
    		len[i] = std::min(B * (B - 1), l[0] - 1);
    	} else if (i == m) {
    		len[i] = std::min(B * (B - 1), n - r[i - 1]);
    	} else {
    		len[i] = std::min(B * (B - 1), l[i] - r[i - 1] - 1);
    	}
    }

    int K = std::accumulate(len.begin(), len.end(), 0);
    std::vector<i64> sum(K + 1);
    for (int i = 0, x = 1; i < m; ++ i) {
    	x += len[i];
    	sum[x] += r[i] - l[i] + 1;
    }

    for (int i = 1; i <= K; ++ i) {
    	sum[i] += sum[i - 1];
    	// std::cout << sum[i] << " \n"[i == K];
    }

    std::vector<int> f(K + 1);
    f[1] = 1;
    for (int i = 1, j = 1; i <= K; ++ i) {
    	j = std::max(j, i + 1);
    	while (j <= K && sum[j] - sum[i] + j - i < A) {
    		++ j;
    	}

    	if (!f[i]) {
    		continue;
    	}

    	for (; j <= K && sum[j] - sum[i] + j - i <= B; ++ j) {
    		f[j] = 1;
    	}
    }

    if (f[K]) {
    	std::cout << "Yes\n";
    } else {
    	std::cout << "No\n";
    }
}

G - Simultaneous Kagamimochi 2

赛时想半天写了个线段树二分没搞出来。。。

题意:e的加强版,改为q个询问,每次问一个区间的匹配数

我们知道e题是怎么判断可行性的,对于一个k,我要把[l, l + k - 1] 对应的放到 [r - k + 1, r]上,那么,对于一个i [1, l + k - 1],它要放到 r - k + (i - l + 1)的上面, 如果大于等于2 * ai的最小位置是pi,那么pi 应该要小于等于 r - k + (i - l + 1),整理一下就是 pi - i r - l + 1 - k。也就是说,如果k合法,那么对于每个i [1, l + k - 1], 都有 pi - i r - l + 1 - k。
那么我们可以二分这个k,然后维护[l, r]区间内的pi-i的最大值,随便用个可以维护区间值的数据结构就行,我用的线段树。

点击查看代码
#define ls (u << 1)
#define rs (u << 1 | 1)

struct Node {
	int l, r;
	int max;
};

struct SegmentTree {
    std::vector<Node> tr;
    SegmentTree(int _n) {
    	tr.assign(_n << 2, {});
    	build(1, 1, _n);
    }

    void build(int u, int l, int r) {
    	tr[u] = {l, r};
    	if (l == r) {
            tr[u].max = -1e9;
    		return;
    	}

    	int mid = l + r >> 1;
    	build(ls, l, mid); build(rs, mid + 1, r);
    }

    void modify(int u, int p, int x) {
    	if (tr[u].l == tr[u].r) {
    		tr[u].max = x;
    		return;
    	}

    	int mid = tr[u].l + tr[u].r >> 1;
    	if (p <= mid) {
    		modify(ls, p, x);
    	} else {
    		modify(rs, p, x);
    	}

    	tr[u].max = std::max(tr[ls].max, tr[rs].max);
    }

    int query(int u, int l, int r) {
    	if (l <= tr[u].l && tr[u].r <= r) {
    		return tr[u].max;
    	}

    	int mid = tr[u].l + tr[u].r >> 1;
    	if (r <= mid) {
    		return query(ls, l, r);
    	} else if (l > mid) {
    		return query(rs, l, r);
    	} else {
    		return std::max(query(ls, l, r), query(rs, l, r));
    	}
    } 
};


void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++ i) {
    	std::cin >> a[i];
    }

    SegmentTree tr(n);
    for (int i = 1; i < n; ++ i) {
        int p = std::lower_bound(a.begin(), a.end(), a[i] * 2) - a.begin();
        tr.modify(1, i, p - i);
    }

    int q;
    std::cin >> q;
    while (q -- ) {
        int L, R;
        std::cin >> L >> R;
        int l = 1, r = (R - L + 1) / 2;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (tr.query(1, L, L + mid - 1) <= R - L + 1 - mid) {
                l = mid;
            } else {
                r = mid - 1;
            }
        }

        int ans = l;
        if (tr.query(1, L, L + l - 1) > R - L + 1 - l) {    
            ans = 0;
        }
        std::cout << ans << "\n";
    }
}
posted @   maburb  阅读(116)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示