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;
}