IAEPC Preliminary Contest (Codeforces Round 999, Div. 1 + Div. 2)


A. Kevin and Arithmetic

题意:给你n个数,你一开始有一个x=0,每次你让x加上一个没用过的数,然后x会一直除二直到变成奇数。如果你加上一个数后能除2,分数加1,问分数最大多少。

奇数后面加奇数才能是偶数,但一开始x是零,那么需要一个偶数,否则只能浪费一个奇数。
所以如果有偶数就是奇数个数加1,否则是奇数个数减1.

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

    if (cnt[0]) {
    	std::cout << 1 + cnt[1] << "\n";
    } else {
    	std::cout << std::max(0, cnt[1] - 1) << "\n";
    }
}

B. Kevin and Geometry

题意:n个数,你要选四个数组成等腰梯形。

对梯形作高发现两边是两个三角形,那么(上底-下底)/2是底边长,为了让梯形的两个腰边能够上上底,这个距离一定小于腰边。所以我们找两个相同的数作为腰边,然后找差值最小的两个数当上底和下底。

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

    std::sort(a.begin(), a.end());

    int p = -1;
    for (int i = 0; i + 1 < n; ++ i) {
    	if (a[i] == a[i + 1]) {
    		p = i;
    		break;
    	}
    }

    if (p == -1) {
    	std::cout << -1 << "\n";
    	return;
    }

    for (int i = 0, j = 0; i + 1 < n; ++ i) {
        while (i == p || i == p + 1) {
            ++ i;
        }

        j = std::max(j, i);
        while (j == i || j == p || j == p + 1) {
            ++ j;
        }

        if (i < n && j < n && std::abs(a[i] - a[j]) < a[p] + a[p]) {
            std::cout << a[j] << " " << a[i] << " " << a[p] << " " << a[p] << "\n";
            return;
        }
    }

    std::cout << -1 << "\n";
}

C. Kevin and Puzzle

题意:n个人,每个人可能是老实人也可能是说谎者,老实人一定说真话。两个说谎者不能站一起。第i个说左边有ai个骗子。问这些人可能的组合有多少。

fi0/1表示第i个是老实人/说谎者。那么如果i是老实人,看前面一个人怎么转移过来。如果前面那个人也是老实人,那么ai=ai+1,因为i说真话,i1也是老实人,那么i1左边的老实人应该等于ai说的,不然两个人不可能同时是老实人。如果i1是说谎者,那么因为两个说谎者不能站一起,第i2个人一定是老实人,因为i1是说谎者,那么到i2这里还有ai1个说谎者,看能不能对上就行。
如果i说谎,那么i1一定使老实人,fi0=fi11

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

    if (n == 1) {
        std::cout << (1 + (a[1] == 0)) << "\n";
        return;
    }

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

        if (i >= 2 && a[i] - 1 == a[i - 2]) {
            f[i][0] += f[i - 1][1];
        }

        f[i][1] = f[i - 1][0];
    }

    std::cout << f[n][0] + f[n][1] << "\n";
 }

D. Kevin and Numbers

题意:两个数字AB,每次可以从A里选两个数,满足他们的差绝对值小于等于1,然后把这两个数删掉,然后把他们的和加入A,问A能不能变成B

考虑反着来,从BA,那么发现如果B的最大值大于A的最大值,那么这个最大值必须拆开,发现拆开的方式是唯一的。于是模拟就行。
注意最多操作nm次,一直操作可能超时。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::priority_queue<int> a, b;
    for (int i = 0; i < n; ++ i) {
    	int x;
    	std::cin >> x;
        a.push(x);
    }

    for (int i = 0; i < m; ++ i) {
    	int x;
    	std::cin >> x;
        b.push(x);
    }

    i64 sum = n - m;

    while (sum >= 0 && a.size() && b.size()) {
    	if (a.top() == b.top()) {
            a.pop();
            b.pop();
        } else if (sum > 0) {
            int u = b.top(); b.pop();
            b.push(u / 2);
            b.push(u - u / 2);
            -- sum;
        } else {
            break;
        }
    }

    if (a.empty() && b.empty()) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

E. Kevin and And

题意:n个数,你有m个数让他们操作,每次可以选一个ij,然后ai&=bj,求最多k次操作让数组和最小。

发现m很小,枚举所有操作可以与的数。 然后计算fij表示第ai操作j次最多减小多少,然后差分一下,记di,jai操作j次比操作j1次可以多减少的值。就可以用优先级队列记录一次操作可以减少的最大值。

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

    std::vector<i64> b(m);
    for (int i = 0; i < m; ++ i) {
    	std::cin >> b[i];
    }

    std::vector<std::vector<i64> > op(m + 1);
    for (int i = 0; i < 1 << m; ++ i) {
        i64 x = (1ll << 30) - 1;
        int cnt = 0;
        for (int j = 0; j < m; ++ j) {
            if (i >> j & 1) {
                x &= b[j];
                ++ cnt;
            }
        }

        op[cnt].push_back(x);
    }

    std::vector d(n, std::vector<i64>(m + 1));
    for (int i = 0; i < n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            for (auto & x : op[j]) {
                d[i][j] = std::max(d[i][j], a[i] - (a[i] & x));
            }

            d[i][j] = std::max(d[i][j], d[i][j - 1]);
        }

        for (int j = m; j >= 1; -- j) {
            d[i][j] = d[i][j] - d[i][j - 1];
        }
    }

    std::priority_queue<std::array<i64, 3> > heap;
    for (int i = 0; i < n; ++ i) {
        heap.push({d[i][1], 1, i});
    }

    i64 ans = std::accumulate(a.begin(), a.end(), 0ll);
    while (k -- ) {
        auto [x, cnt, id] = heap.top(); heap.pop();
        ans -= x;
        if (cnt + 1 <= m) {
            heap.push({d[id][cnt + 1], cnt + 1, id});
        }
    }

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

F1. Kevin and Binary String (Easy Version)

题意:给你两个01串s,t,你可以交换s中两个相邻的块,块是指全由相等元素组成的且左右不能再扩展的区间。求使得s=t的最小操作数。

方案是固定的,所以模拟就好。
发现每个块交换后就会和其他块融合,而我们想要满足s=t肯定要按顺序操作,从前到后满足。于是把s,t中连续的0和1都存在一个数字,代表这个块里有几个元素,0的块是负数,1的块是正数。这样就可以模拟了,如果s的当前第一个块和t的当前块不一样,则先交换一下使得一样,然后一直交换使得个数一样即可。

点击查看代码
void solve() {
    std::string s, t;
    std::cin >> s >> t;
    int n = s.size();
    std::deque<int> a;
    std::vector<int> b;
    for (int i = 0; i < n; ++ i) {
    	int j = i;
    	while (j < n && s[j] == s[i]) {
    		++ j;
    	}

    	a.push_back((s[i] == '0' ? -1 : 1) * (j - i));
    	i = j - 1;
    }

    for (int i = 0; i < n; ++ i) {
    	int j = i;
    	while (j < n && t[j] == t[i]) {
    		++ j;
    	}

    	b.push_back((t[i] == '0' ? -1 : 1) * (j - i));
    	i = j - 1;
    }

    int ans = 0;
    for (auto & x : b) {
    	if (a.empty()) {
    		std::cout << -1 << "\n";
    		return;
    	}

    	if (1ll * a.front() * x < 0) {
    		if (a.size() == 1) {
    			std::cout << -1 << "\n";
    			return;
    		}

    		if (a.size() == 2) {
    			std::swap(a[0], a[1]);
    		} else {
    			a[2] += a[0];
    			a.pop_front();
    		}

    		++ ans;
    	}

    	int m = std::abs(x) - std::abs(a.front());
    	int k = 0;
    	a.pop_front();
    	while (m > 0) {
    		if (a.empty()) {
    			std::cout << -1 << "\n";
    			return;
    		}

    		k += a.front();
    		a.pop_front();

    		if (a.empty()) {
    			std::cout << -1 << "\n";
    			return;
    		}

    		m -= std::abs(a.front());
    		a.pop_front();
    		++ ans;
    	}

    	if (m < 0) {
    		std::cout << -1 << "\n";
    		return;
    	}

    	if (a.empty()) {
    		a.push_back(k);
    	} else {
    		a[0] += k;
    	}
    }

    std::cout << ans << "\n";
}
posted @   maburb  阅读(103)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
点击右上角即可分享
微信分享提示