Codeforces Round 973 (Div. 2)

A. Zhan's Blender

\(n\) 个水果,每秒可以消耗 \(\min\{x, y\}\) 个,问需要多少时间。

整数除法的上取整操作即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

void solve()
{
	int n = read(), x = read(), y = read();
	int xx = min(x, y);
	printf("%d\n", (n + xx - 1) / xx);
}

int main()
{
	int T = read();
	while(T--) solve();
    return 0;
}

B. Battle for Survive

\(n\) 个人,每个人有一个分数 \(a_i\),每次选择两个人 \(i, j\) 满足 \(i < j\)\(i\) 被打败退役,\(j\) 的分数变为 \(a_j - a_i\),问最后的冠军的分数最大为多少。

发现第 \(n\) 个人一定是冠军,第 \(n - 1\) 个人一定是要退役的,若 \(i < j < k\)\(i\) 先和 \(j\) 打,\(j\) 再和 \(k\) 打,最终得分为 \(a_k - (a_j - a_i)\),相当于给 \(() a_1 () a_2 \cdots () a_{n-2} - a_{n-1} + a_n\) 中前 \(n - 2\) 个数赋上正负号,使得最终和最大,显然都赋正号最大。

也就是先让前 \(n - 2\) 个人和 \(n - 1\) 个人打,然后让 \(n\) 去报仇。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int a[N];

void solve()
{
	int n = read();
	for(int i = 1; i <= n; ++i) a[i] = read();
	ll ans = a[n] - a[n - 1];
	for(int i = n - 2; i >= 1; --i) ans += a[i];
	printf("%lld\n", ans);
}

int main()
{
	int T = read();
	while(T--) solve();
    return 0;
}

C. Password Cracking

交互题,给定一个长度 \(n\),答案是长度为 \(n\)\(01\) 串,每次可以询问一个长度不大于 \(n\)\(01\) 串是否是答案串的子串。询问次数不超过 \(2n\) 次。

如果已知一个串是答案串的子串,那么尝试在它后面加上 \(0\) 或者 \(1\),如果都不能加说明这个串是答案串的一个后缀,再尝试在前面加。

注意到在前面加 \(0\)\(1\) 时实际上只需要询问一次。

发现分为两个过程,如果只做第一个过程就可以找到串,询问次数最多不超过 \(2n\) 次。

当需要第二个过程时,设第 \(i\) 次无法从当前串后面加,那么询问最多不超过 \(2i + 1 + n - i + 1 = n + i + 2\) 次,实际上构造不出卡满询问次数的串。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long


void solve()
{
	int n;
	cin >> n;
	string s, t;
	s.clear(), t.clear();
	int len = 0, flag = 0;
	for(len = 1; len <= n; ++len)
	{
		t = s + "0";
		cout << "? " << t << '\n';
		cout.flush();
		int flag1;
		cin >> flag1;
		if(flag1){ s = s + "0"; continue; }
		t = s + "1";
		cout << "? " << t << '\n';
		cout.flush();
		int flag2;
		cin >> flag2;
		if(flag2){ s = s + "1"; continue; }
		flag = 1; break;
	}
	if(flag)
	{
		for(; len <= n; ++len)
		{
			t = "0" + s;
			cout << "? " << t << '\n';
			cout.flush();
			int flag1;
			cin >> flag1;
			if(flag1){ s = "0" + s; continue; }
			s = "1" + s;
		}
	}
	cout << "! " << s << '\n';
	cout.flush();
}

int main()
{
	int T;
	cin >> T;
	while(T--) solve();
    return 0;
}

D. Minimize the Difference

给定长度为 \(n\) 的序列,每次操作选择位置 \(i\) 使得 \(a_i = a_i - 1, a_{i + 1} = a_{i + 1} + 1\),可以操作任意次,求最终序列 \(\max\{ a_1, a_2, \cdots a_n \} - \min\{ a_1, a_2, \cdots a_n \}\) 的最小值。

最理想的想法就是均摊总和,但是每次只能从低位向高位加 \(1\),设 \(pre_i, suf_i\) 分别表示前缀和和后缀和。

在均摊的情况下:

\(i\) 个数中,最小的数不会小于 \(\lfloor \frac{pre_i}{i} \rfloor\),可以求出整个的序列的最小值的最大值。

\(i\) 个数中,最大的数不会超过 \(\lceil \frac{suf_{n-i+1}}{i} \rceil\),可以求出整个序列的最大值的最小值。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

ll read()
{
    ll x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n;
ll a[N], sum1[N], sum2[N];

void solve()
{
	n = read();
	for(int i = 1; i <= n; ++i) a[i] = read(), sum1[i] = sum1[i - 1] + a[i];
	sum2[n + 1] = 0;
	for(int i = n; i >= 1; --i) sum2[i] = sum2[i + 1] + a[i];
	ll mx = 0, mn = 0x7fffffffffffffff;
	for(int i = 1; i <= n; ++i) mn = min(mn, sum1[i] / (ll)i), mx = max(mx, (sum2[i] + n - i) / (ll)(n - i + 1));
	// printf("mx = %lld, mn = %lld\n", mx, mn);
	printf("%lld\n", mx - mn);
}

int main()
{
	int T = read();
	while(T--) solve();
    return 0;
}

E. Prefix GCD

给一个长度为 \(n\) 的序列重新排列,使得前缀 \(\operatorname{gcd}\) 的和最小。

每次选择一个可以使 \(\operatorname{gcd}\) 最小的数加入,直到等于 \(\operatorname{gcd}\{ a_1, a_2, \cdots a_n \}\)时停止操作。

由于值域的和不超过 \(10^5\),每次加入一个数至少使 \(\operatorname{gcd} / 2\),做 \(\log\) 次即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 1e5 + 5;
int n, a[N];

int gcd(int x, int y)
{
	if(!x || !y) return x | y;
	return gcd(y, x % y);
}

void solve()
{
	n = read();
	int GCD = 0, nowgcd = 0;
	for(int i = 1; i <= n; ++i) a[i] = read(), GCD = gcd(GCD, a[i]);
	ll ans = 0;
	for(int i = 1; ; ++i)
	{
		int mn = 0x7fffffff;
		for(int j = 1; j <= n; ++j) mn = min(mn, gcd(nowgcd, a[j])); 
		nowgcd = mn, ans += nowgcd;
		if(nowgcd == GCD){ ans += 1ll * (n - i) * GCD; break; }
	}
	printf("%lld\n", ans);
}

int main()
{
	int T = read();
	while(T--) solve();
    return 0;
}

F1. Game in Tree (Easy Version)

有一个以 \(1\) 为根节点的树,最初Alice在 \(1\) 点,Bob在 \(u\) 点,两个人轮流走,每一步只能选择走向一个没有被走过的节点,不能走的人输,Alice先走,每个人都采用最优策略,问最后的获胜者。

\(1\)\(u\) 路径上的点取出,即为 \(p_1, p_2, \cdots , p_{m}\)\(p_1\) 即为 \(1\)\(p_m\) 即为 \(u\)

\(dp[x]\) 表示从节点 \(x\) 向子树走,且不经过 \(1\)\(u\) 路径上的点(\(x\) 可以在路径上),能经过的最大点数。

\(a_i = dp[p_i] + depth[p_i] - 1\)(根节点深度为 \(1\))。

\(b_i = dp[p_i] + depth[u] - depth[p_i]\)

容易发现 \(a_i, b_i\) 就是Alice和Bob走到 \(p_i\) 并走向某一颗不在路径上的子树的能经过的最大点数。

考虑当两个人分别在 \(p_i, p_j\) 时,到Alice先走,如何有必胜策略?

\(a_i > \max\{ b_{i+1}, \cdots , b_{j} \}\) 时,无论Bob怎么走都会输掉比赛。

同理,当 \(b_j \ge \max{a_i, \cdots, a_{j-1}}\) 时,无论Alice怎么走都会输掉比赛。

(由于Alice比Bob先走,所以当两个人能经过的点数相同时,Bob会胜利)。

若当前步没有必胜策略时,说明对方有绝杀我的方法,所以我要沿着路径走,尽可能抢占对方的决胜点。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int inf = 0x7fffffff;
const int N = 2e5 + 5;
int n;
vector<int> V[N];
int f[N], depth[N], vis[N], dp[N];

void DFS(int k, int fa)
{
    f[k] = fa, depth[k] = depth[fa] + 1;
    for(auto v : V[k]) if(v != fa) DFS(v, k);
}

void dfs(int k, int fa)
{
	dp[k] = 1;
	for(auto v : V[k])
    {
        if(v == f[k]) continue;
        dfs(v, k);
        if(!vis[v]) dp[k] = max(dp[k], dp[v] + 1);
    }    
}

int sta[N], top;
int lg[N], stA[20][N], stB[20][N];

int get(int l, int r, int d)
{
    int k = lg[r - l + 1];
    if(d == 0) return max(stA[k][l], stA[k][r - (1 << k) + 1]);
    else return max(stB[k][l], stB[k][r - (1 << k) + 1]);
}

void solve()
{
	n = read();
	for(int i = 1; i <= n; ++i) V[i].clear(), vis[i] = 0;
	for(int i = 1; i < n; ++i)
	{
		int u = read(), v = read();
		V[u].emplace_back(v);
		V[v].emplace_back(u);
	}
    DFS(1, 0);
	int x = read(), y = read();
	while(top) sta[top] = 0, --top;
	while(y) vis[y] = 1, sta[++top] = y, y = f[y];
	dfs(1, 0);

    stA[0][1] = -inf, stB[0][top] = -inf;
    for(int i = 2; i <= top; ++i) stA[0][i] = dp[sta[i]] + depth[sta[i]] - 1;
    for(int i = 1; i < top; ++i) stB[0][i] = dp[sta[i]] + depth[x] - depth[sta[i]];
    
    for(int i = 1; i <= 19; ++i)
        for(int j = 1; j + (1 << i) - 1 <= top; ++j)
            stA[i][j] = max(stA[i - 1][j], stA[i - 1][j + (1 << (i - 1))]),
            stB[i][j] = max(stB[i - 1][j], stB[i - 1][j + (1 << (i - 1))]);

    // 0是Alice,1是Bob
    int op = 0, Apos = top, Bpos = 1;
    while(Bpos < Apos)
    {
        if(op == 0)
        {
            if(get(Apos, Apos, 0) > get(Bpos, Apos - 1, 1))
            {
                printf("Alice\n"); return ;
            }else Apos--;
        }else
        {
            if(get(Bpos, Bpos, 1) >= get(Bpos + 1, Apos, 0))
            {
                printf("Bob\n"); return ;
            }else Bpos++;
        }
        if(Apos == Bpos)
        {
            if(op == 0) printf("Bob\n");
            else printf("Alice\n");
            return ;
        }
        op ^= 1;
    }
}

int main()
{
    lg[0] = -1;
    for(int i = 1; i <= 200000; ++i) lg[i] = lg[i >> 1] + 1;
	int T = read();
	while(T--) solve();
    return 0;
}

F2. Game in Tree (Hard Version)

有点神,先咕着。


posted @ 2024-09-23 17:47  梨愁浅浅  阅读(194)  评论(0编辑  收藏  举报