Codeforces Round #781 (Div. 2) A—C
Codeforces Round #781 (Div. 2)
https://codeforces.com/contest/1665
我懒得写题意了,这场题目理解难度不大,构造难度大
A GCD vs LCM
思路
用 1 来构造(后面三个都是1,剩下的丢到第一个那里去
以后碰到LCM 和 GCD 的题目都可以往1去凑凑。
Code
#include <bits/stdc++.h>
using namespace std;
int main () {
int t;
cin >> t;
while (t --) {
int n;
cin >> n;
cout << n - 3 << " 1 1 1" << endl;
}
}
B - Array Cloning Technique
思路
有点倍增的思想,就是每次都可以把序列当中的 最长相同数字 从其它数列里面给挪过来。
如果无法凑齐,就复制一遍,次数++
(我一开始以为每次复制都只能copy最原始的数列,所以错了)
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int main () {
int t;
cin >> t;
while (t --) {
int n, maxn = 0;
cin >> n;
map<int, int> mp;
for (int i = 1; i <= n; i ++) {
int x;
cin >> x;
mp[x] ++;
maxn = max (maxn, mp[x]);
}
if (maxn == n) {
cout << 0 << endl;
continue;
}
int ans = 1;
n -= maxn; //出去最大的外,有多少不同
//先要copy一次
ans += min (n, maxn);
n -= maxn;
while (n > 0) {
maxn *= 2, ans ++; //copy
ans += min (n, maxn); //剩下的 or 成倍复制
n -= maxn; //copy
}
cout << ans << endl;
}
}
//找最长的那个,每次把两个它移进来
//注意是倍增的,不需要每两次一次
//每次复制后,取原排列中重复次数最多的数乘2到符合题意为止
C - Tree Infection
思路
有两种操作:
- Spreading: 把所有已经易染色的节点的兄弟节点同时染上色 (注意:染多个点只消耗一次操作,但是这多个点不可以互为兄弟)
- Injection: 对于任意一个节点进行染色
由此观之,1.的效率更高,所以先来操作1.
而操作顺序是,先染兄弟节点较多的,这也好理解(兄弟多的到了后面也会被染色,捎带上兄弟少的一起)
具体操作细节请看Code及注释(已经写到让我这种苯人都能理解清楚的程度了)
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, cnt, sib[N]; //sib[]兄弟节点
vector<int>v[N];
void init () {
for (int i = 1; i <= n; i ++)
v[i].clear();
sib[1] = 0; //根节点没有兄弟
cnt = 1; //根节点染色
}
bool check (int x) {
int res = 0;
for (int i = 1, j = x - 1; i <= cnt; i ++, j --)
res += max (0, sib[i] - j); //有可能为负
return x - cnt >= res; //x总操作次数,cnt是spreading的次数
}
int main () {
int t; cin >> t;
while (t --) {
cin >> n;
init(); //necessary
for (int i = 2; i <= n; i ++) {
int x;
cin >> x; //输入父亲
v[x].push_back (i); //记录所有的儿子
}
for (int i = 1; i <= n; i ++)
if (!v[i].empty())
sib[++ cnt] = v[i].size() - 1;
sort (sib + 1, sib + cnt + 1, greater<int>());
int l = cnt, r = n; //最多也就是每个点都染一次
//最大值最小,二分
while (l < r) {
int mid = l + r >> 1;
if (check (mid))
r = mid; //求最少的次数
else
l = mid + 1;
}
cout << r << endl;
}
}