CFR-838-Div-2解题报告

比赛传送门

A. Divide and Conquer

题意:给你一个数组,每次操作可以将一个数变为它除以二下取整,求将数组的和变为偶数的最小次数。

显然如果数组本来就是偶数,则为 \(0\),否则一定是选一个数一直除到改变,而其他数不动(动了显然更劣)。于是对于每个数求改变奇偶的最小次数,模拟即可。

By jiangly

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    int par = 0;
    int mn = 1E9;
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        par ^= a[i] & 1;
        int x = a[i], t = 0;
        while (x % 2 == a[i] % 2) {
            x /= 2;
            t++;
        }
        mn = std::min(mn, t);
    }

    std::cout << (par ? mn : 0) << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

B. Make Array Good

题意:有一个数组,每次可以将一个数 \(x\) 改为 \([x,2x]\) 内的任意整数,求在 \(n\) 次以内将这个数组改为任意两个数均有倍数关系的方案。

由于在 \([x,2x]\) 内,容易想到让它们均变为二的幂(此时一定合法)。于是对于每个数改为比他大的最近的二的幂即可。可以使用 \(2^{\lfloor\log_2(n)\rfloor+1}\) 来快速得出。

By jiangly

#include <bits/stdc++.h>

using i64 = long long;

void solve() {
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    std::cout << n << "\n";
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        int p = 1 << (std::__lg(a[i]) + 1);
        std::cout << i + 1 << " " << p - a[i] << "\n";
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

C. Binary Strings are Fun

给你一个 \(01\) 串,对于每个前缀,计算生成合法串的方案数,输出它们的和(对 \(998244353\) 取模)。生成一个合法串,需要在每相邻两位之间插入一个新位,使得任意一个奇数前缀的中位数为末尾的数字。

首先举例子找一找规律。假设待生成的串为 0_0_1_1_0_1_1_1,下划线是需要填的。我们依此考虑:只考虑前三位,中位数为 \(0\),所以第一个空填 \(01\) 都可以;考虑前五位,中位数为 \(1\),所以第一个和第二个空都必须填 \(1\);前七位,第四个空 01 都可以;前九位,则第三个和第四个空只能填 \(0\)

依此类推,我们发现,如果某一个“限制位置”和下一个“限制位置”不同,那么中位数在这两位后发生改变,所以这两位之间的空位必须填后面的值,且前面这两位的数量差只能为 \(1\)。如果某一个限制位置和下一个限制位置相同,那么必须填与它们相反的值,因为如果与它们相同,则数量差超过 \(1\),而后面一个不同的位置就会无法填。

所以,只有一个位置后面没有不同位置时,才可以真正的任意填,否则只有一种方案。此时答案就很显然了,即为 \(2\) 的(最后一段连续的长度 \(-1\))次方。

By jiangly

void solve() {
    int n;
    std::cin >> n;

    std::string s;
    std::cin >> s;

    int last = 0;
    Z ans = 0, sum;

    for (auto c : s) {
        if (last != c) ans = 1;
        else ans *= 2;
        last = c;
        sum += ans;
    }

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

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t;
    std::cin >> t;

    while (t--) {
        solve();
    }

    return 0;
}

D. GCD Queries

题意:交互题。有一个 \(0\sim n-1\) 的排列,每次可以询问两位的 \(\gcd\),在 \(2n\) 次操作内给出两个位置,使得它们中的一个位置为 \(0\)

首先有一个非常重要的性质:\(\forall y,\gcd(x,0)\ge \gcd(y,0)\)。所以我们可以依此扫描每个位置,维护当前扫到的可能为 \(0\) 的两个位置 \(x,y\)

初始时 \(x=1,y=2\),从 \(3\) 开始,对于每个位置 \(i\),询问 \(\gcd(p_x,p_i)\)\(\gcd(p_y,p_i)\),如果两个答案相同,那么 \(p_i\) 不可能是 \(0\)(因为 \(\gcd(0,x)=x\),而排列里 \(x\) 互不相同);如果两个答案不同,那么 \(x,y\) 中较小的那个一定不是 \(0\)(因为前面的性质),而 \(p_i\) 有可能是 \(0\),将较小的那个改为 \(i\) 即可。

By cxm1024

#include<bits/stdc++.h>
using namespace std;
signed main() {
	int t;
	cin>>t;
	while(t--) {
		int n;
		cin>>n;
		int now=2,x=1,y=2,xx,yy;
		while(now<n) {
			now++;
			cout<<"? "<<x<<" "<<now<<endl;
			cin>>xx;
			cout<<"? "<<y<<" "<<now<<endl;
			cin>>yy;
			if(xx>yy) y=now;
			else if(yy>xx) x=now;
		}
		cout<<"! "<<x<<" "<<y<<endl;
		cin>>n;
		assert(n==1);
	}
	return 0;
}
posted @ 2023-03-01 15:51  曹轩鸣  阅读(10)  评论(0编辑  收藏  举报