CF Div2 781

这篇博客探讨了三类问题的解决方案:一是利用数论性质找到四个数使得它们的最大公约数等于它们的最小公倍数;二是通过贪心策略和模拟操作解决数组克隆与交换次数最小化问题;三是针对树形结构的病毒感染问题,通过贪心算法确定最短感染时间。这些问题展示了数学和算法在实际问题解决中的智慧和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A GCD vs LCM(数论 + 思维)

Description:

​ 求4个和为n的数 要求 gcd(a, b) == lcm(c, d)

Solution:

​ 令lcm(c, d) = 1 则 c = 1 d = 1

​ 因为x取任意正整数 gcd(1, x) = 1始终成立

​ 令gcd(a, b) = 1 则 a = 1 b = n - 3

Code:

void solve()
{
	int n; cin >> n;
	cout << 1 << ' ' << n - 3 << ' ' << 1 << ' ' << 1 << endl;
}

B Array Cloning Technique(模拟)

Description:

​ 现有长度为n的数组 你可以通过若干次操作 使某个数组中的所有数相同

​ 操作一 选择一个数组,将其复制一份,即新增一个一模一样的数组。

​ 操作二 选择数组a 和 数组b 交换a_i和b_i

​ 求最少的操作次数

Solution:

​ 贪心:现有数组中出现最多的数为x 我们应将数组全部置为x

​ 每次复制会获得比上一次复制*2的交换机会

​ 模拟 复制和交换并累加次数

Code:

void solve()
{
	int n; cin >> n;
	map<int, int> mp;
	int x;
	for(int i = 1; i <= n; i++)
		cin >> x, mp[x] ++;
	
	int mx = 0;
	for(auto x : mp)
		mx = max(mx, mp.second);
	
	if(n == 1) {cout << 0 << '\n'; return;}
	if(mx == n) {cout << 0 << '\n'; return;}
	
	int t = n - mx, st = mx, tot = 0, res = 0;
	while(tot < t)
	{
		res ++; //复制
		tot += st, cnt += st;
		st *= 2;
	}
	if(tot > t)	res -= (tot - t); //加多了
	cout << res << '\n';
}

C Tree Infection(贪心 + 模拟)

Description:

​ 给定一棵树 每秒做两个操作

​ 先在对于所有感染节点 传染一下病毒 然后你可以感染任一未被感染的节点

​ **传染:**如果一个节点的子节点中有儿子被感染了 那么他就会传染他的一个兄弟

​ 请输出最短感染整棵树的时间

Solution:

​ 因为传染只能在兄弟之间进行 所以呢 我们要统计出一共有多少组兄弟 每组必须感染一遍

​ 我们就用桶排来存一下 sum[i] 代表父节点i的儿子群数量(兄弟群)

​ 然后排序一下 就可以得到有m组 答案最少为m

​ 贪心:因为每秒会传染一次 所以先感染健康人数最多的组 就可以让他们尽可能多地被感染

​ 在感染完一波的时候 我们看一下还剩多少人 再重排一遍

​ 在传染的基础上 为健康人数最多的组感染一个 当感染整棵树的时候 结束

Code:

void solve()
{
    int n;
    cin >> n;
    ms(sum, 0);
    sum[n + 1] = 1; //感染根节点
    for(int i = 2; i <= n; i++)
    {
        int x; cin >> x;
        sum[x] ++;
    }
    sort(sum + 1, sum + n + 2, greater<int>());
    
    int m; //一共有多少个树群
    for(m = 1; ; m++)
    {
        if(sum[m] == 0)
        {
            m --;
            break;
        }
    }

    for(int i = 1; i <= m; i++)
        sum[i] = sum[i] - m + i - 1; //先感染大的 贪心 计算传染的人数
    
    sort(sum + 1, sum + m + 1, greater<int>()); //把剩下的重排 大到小
    int mx = sum[1] > 0 ? 1 : 0; //如果连最大的都已经是0 那就结束辣
    int res = m; //最小的可能答案是m
    while(1)
    {
        if(sum[mx] == 0)    break; //已经找不到健康人数最多的点了
        sum[mx] --; //手动感染
        res ++;
        mx = 0; //sum[mx] = 0;
        for(int j = 1; j <= m; j++)
        {
            if(sum[j] <= 0) break;
            sum[j] --; //传染
            if(sum[mx] < sum[j])    mx = j; //找到当前健康人数最多的点
        }
    }
    cout << res << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值