「CF#554 div2」题解

本文解析了五道经典算法题,包括集合数对求和、数位操作、最小公倍数优化、动态规划问题及欧拉路径应用,涵盖了从贪心策略到复杂图论的多种算法技巧。

A

水题一道。
题目的大致意思就是:给你两个集合,求集合间有多少数对和是奇数。

题解,开444个桶后,求一个minminmin就可以了。

#include <bits/stdc++.h>
using namespace std;
int n, m;
int v1[4], v2[4];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1, x; i <= n; i ++) scanf("%d", &x), v1[x % 2] ++;
	for (int i = 1, x; i <= m; i ++) scanf("%d", &x), v2[x % 2] ++;
	cout << min(v1[0], v2[1]) + min(v1[1], v2[0]) << endl; 
	return 0;
}

B

题目大意

给你一个数xxx,并给你两个操作AAABBB,要求操作AAABBB必须交替执行。
操作AAA:给一个2k−12^k-12k1异或当前xxx
操作BBB:将当前的数+1+1+1
让你在404040步内完成以下的任务:让你把xxx变成2k−12^k-12k1

题解

水题一道
我们考虑贪心。
首先需要了解操作AAA的本质。
其实就是在二进制下异或一个111⋯1111\cdots11111的数。
那么为了处理到所有的000变成111,那么我们需要考虑第一个000,也就是在二进制下最高位的000
比较明显的贪心策略就出现了,每一次我们异或掉最高位的000,然后+1+1+1
10610^{6}106很明显可以在404040步以内完成,所以不存在无法完成的情况。

代码

#include <bits/stdc++.h>
using namespace std;
int x, n = 0, ans = 0, pos, cnt = 0;
int a[30], Ans[30];
bool fg;
int main() {
    cin >> x;
    for (; x; x >>= 1) a[++ n] = x % 2;
    for (int i = 1; i <= n; i ++) {
        fg = 1;
        for (int j = 1; j <= n; j ++) if (a[j] == 0) { fg = 0; break; }
        if (fg) break; 
        for (int j = n; j; j --) {
            if (a[j] == 0) { pos = j; break; }
        }
        for (int j = 1; j <= pos; j ++) a[j] ^= 1;
        Ans[++ cnt] = pos; ++ ans;
        fg = 1;
        for (int j = 1; j <= n; j ++) if (a[j] == 0) { fg = 0; break; }
        if (fg) break; 
        a[1] ++;
        for (int j = 1; j <= n; j ++) if (a[j] > 1) a[j] = 0, a[j + 1] ++; 
        ++ ans;
    }
    cout << ans << endl;
    for (int i = 1; i <= cnt; i ++) printf("%d ", Ans[i]);
    return 0;
}

C

题目大意

给你aaabbb,求最小的kkk使a+ka+ka+kb+kb+kb+k的最小公倍数最小。

题解

数论水题一道。
lcm(a+k,b+k)=(a+k)×(b+k)gcd(a+k,b+k)lcm(a+k,b+k)=\frac{(a+k)\times (b+k)}{gcd(a+k,b+k)}lcm(a+k,b+k)=gcd(a+k,b+k)(a+k)×(b+k)
辗转相减法可得:gcd(a+k,b+k)=gcd(a+k,b−a)gcd(a+k,b+k)=gcd(a+k,b-a)gcd(a+k,b+k)=gcd(a+k,ba)
那么很明显的是b−ab-aba是一个定值,而且答案和b−ab-aba的约数有关,那么我们就枚举b−ab-aba的约数,然后暴力判断是否满足答案就可以了。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
const ll linf = 1ll << 62; 
ll a, b, c, ans = linf, ret = linf;
void check(ll x) {
	ll ta = ceil((db)a/x)*x;;
	ll tb = ta + c;
	ll tmp = ta * tb / x;
	if (tmp == ret) {
		if (ta - a < ans) ans = tmp;
	} 
	else if (tmp < ret) ans = ta - a, ret = tmp;
}
int main() {
	cin >> a >> b; 
	if (a == b) { cout << 0 << endl; return 0; }
	if (a > b) swap(a, b);
	c = b - a; ll sq = sqrt(c);
	for (int i = 1; i <= sq; i ++) {
		if (c % i == 0) { check(i); check(c / i); }
	}
	cout << ans << endl; 
	return 0;
}

D

简单DP+结论。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000 + 4;
const int P = 1e9 + 7;
int n;
ll f[N][N], ans;
int main() {
    cin >> n;
    f[0][0] = 1;
    for (int i = 0; i <= n; i ++) {
        for (int j = 0; j <= i; j ++) {
            if (i && i >= j + 1) f[i][j] += f[i - 1][j];
            if (j) f[i][j] += f[i][j - 1];
            f[i][j] %= P;
        }
    }
    for (int i = 0; i <= n; i ++) 
        for (int j = 0; j <= i; j ++) 
            if ((i + j) % 2 == 1) ans = (ans + f[i][j]) % P;
    cout << ans << endl; 
    return 0;
}

E

题解

其实还是一道水题。。。
比较容易就可以得出题目中给出PPP数组是没有用的。
输入中给出的b′b&#x27;bc′c&#x27;c一定是相邻的两个数。
也就是b′i{b&#x27;}_ibic′i{c&#x27;}_ici是相邻的。

我们需要还原aaa数组,也就是需要符合以上所有b′b&#x27;bc′c&#x27;c的限制。
容易想到就是用欧拉路径实现。

坑点盘点:

  • 建出的图可能不连通。
  • 可能存在环中路径。
  • 需要离散化。

代码

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 2e5 + 5;
int a[N], b[N], c[N], d[N], disc[N], de[N], point[N], ans[N];
int n, dcnt = 0, acnt = 0, pcnt = 0; 
multiset<int> g[N];
template <typename T> void read(T &x) {
    x = 0; T fl = 1; char ch = 0;
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') fl = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    x *= fl;
}
void dfs(int u) {
	if (de[u] == 0) { ans[++ acnt] = u; return; }
	for (auto i = g[u].begin(); i != g[u].end(); i = g[u].begin()) {
		int v = *i;
		g[u].erase(i); 
		g[v].erase(g[v].find(u)); de[u] --, de[v] --; 
		dfs(v); 
	}
	ans[++ acnt] = u;
}
signed main() {
	read(n);
	for (int i = 1; i <= n; i ++) g[i].clear();
	for (int i = 1; i < n; i ++) read(a[i]), disc[++ dcnt] = a[i];
	for (int i = 1; i < n; i ++) read(b[i]), disc[++ dcnt] = b[i];
	for (int i = 1; i < n; i ++) if (a[i] > b[i]) { cout << -1 << endl; return 0; }
	sort(disc + 1, disc + 1 + dcnt);
	dcnt = unique(disc + 1, disc + 1 + dcnt) - disc - 1;
	for (int i = 1; i < n; i ++) {
		c[i] = lower_bound(disc + 1, disc + 1 + dcnt, a[i]) - disc;
		d[i] = lower_bound(disc + 1, disc + 1 + dcnt, b[i]) - disc;
	}
	for (int i = 1; i < n; i ++) {
		g[c[i]].insert(d[i]); g[d[i]].insert(c[i]);
		de[c[i]] ++; de[d[i]] ++;
	}
	for (int i = 1; i <= n; i ++) 
		if (de[i] & 1) point[++ pcnt] = i;
	if (pcnt != 0 && pcnt != 2) { cout << -1 << endl; return 0; }
	acnt = 0; 
	if (pcnt == 0) dfs(1); else dfs(point[1]);
	if (acnt != n) { cout << -1 << endl; return 0; }
	for (int i = 1; i <= acnt; i ++) printf("%d ", disc[ans[i]]); 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值