Codeforces Round 1060 (Div. 2)
A. Notelock
遍历字符串s,当遇到字符为‘1’时,记录到re当中。我们用last表示上一个字符,遍历re数组,每次比较当前字符位置的前k-1范围内是否存在‘1’字符,若存在,则表明后续存在一个被保护的字符能保护到这里,则last后移,赋值x,若不能被保护,则说明last这个字符后面的字符不能保护到他,则last这个位置必须自己保护,及cnt++,last后移继续遍历。初始值last赋值为-1是因为当前情况下不存在被保护的字符,则第一个‘1’的位置必须自己保护,及cnt++。
查看代码void solve(){ int n, k; string s; cin >> n >> k; cin >> s; s = ' ' + s; vector<int> re; for(int i = 1; i <= n; ++i){ if(s[i] == '1') re.emplace_back(i); } int cnt = 0; int last = -1; for(int& x : re){ int start = max(1, x - k + 1); if(last < start){ cnt++; last = x; }else{ last = x; } } cout << cnt << endl; return ; }
B. Make it Zigzag
题目要求偶数位一定要比两边的奇数位要大,并且操作1(ai:=max(a1,…,ai))不需要任何消耗,所以,我们可以让偶数位都变为最大值,用maxx数组存储前i个数的最大值,最后遍历一遍奇数位,把高于两边的数执行操作2,直至比两边的偶数位都小就行。
查看代码void solve(){ int n; cin >> n; vector<int> a(n + 2,0); for(int i = 1; i <= n; ++i) cin >> a[i]; vector<int> maxx(n + 1, 0); for(int i = 1; i <= n; ++i){ if(a[i] > maxx[i - 1]){ maxx[i] = a[i]; }else{ maxx[i] = maxx[i - 1]; } } for(int i = 2; i <= n; i += 2){ a[i] = maxx[i]; } int cnt = 0; a[0] = 0x3f3f3f3f; a[n + 1] = 0x3f3f3f3f; for(int i = 1; i <= n; i += 2){ if(a[i] < a[i - 1] && a[i] < a[i + 1]) continue; cnt += a[i] - min(a[i - 1], a[i + 1]) + 1; } cout << cnt << endl; return ; }
C1. No Cost Too Great (Easy Version)
先通过埃式筛,筛选前2e5每个数的质因数。C1版本中,每次加一的代价都是1,暂时不需要考虑贪心。题目要求至少有1对数的gcd要大于1,要想使两个数gcd大于1,最多进行两次加1操作就行,若只要加一次1,就是加完1的那个数是另一个数的倍数;若加了两次1,加完后可以让两个数的gcd变为2。首先对于输入的n个数,建立一个哈希表,统计每一个树的质因数数量,若发现某一个质因数的数量大于1,也就是说存在两个数,它们的gcd大于1,因此不需要任何操作,输出0;若不存在数量大于1的质因数,考虑只加一次的操作,依次遍历n个数,让这个数加一,初始时先让未加1的这个数的质因数从哈希表中减去,再把加一后得到的数的质因数加入到哈希表中,若发现质因数数量大于1,也就是在原数组中存在一个数与加一后的数gcd大于1,输出1。若上述两种情况都为产生结果,只能加两次1,使两个数的gcd为2,输出2。
查看代码#include<bits/stdc++.h> #define fi first #define se second using namespace std; #define ll long long const int N = 2e5 + 10; vector<vector<int>> pfac(N); void solve(){ int n; cin >> n; vector<int> a(n), b(n); for(auto& x : a) cin >> x; for(auto& x : b) cin >> x; int ans = 2; map<int, int> cnt; for(int i = 0; i < n; ++i){ for(auto& x : pfac[a[i]]){ if(cnt[x] > 0){ ans = 0; } cnt[x]++; } } for(int i = 0; i < n; ++i){ for(auto& x : pfac[a[i]]){ cnt[x]--; } for(auto& x : pfac[a[i] + 1]){ if(cnt[x] > 0) { ans = min(ans, 1); } } for(auto& x : pfac[a[i]]){ cnt[x]++; } } cout << ans << endl; return ; } signed main(){ cin.tie(nullptr)->sync_with_stdio(false); //筛选质因数 for(int i = 2; i <= N; ++i){ if(!pfac[i].empty()) continue; for(int j = i; j <= N; j += i){ pfac[j].emplace_back(i); } } int T; cin >> T; while(T--){ solve(); } return 0; }
C2. No Cost Too Great (Hard Version)
C2与C1的区别就是b数组的元素不完全相同。算法结构与C1大致相同。对于不修改的情况,就是质因数的数量大于1就行,直接返回答案为0的结果;对于修改1次的情况,求解方式与C1一样,只不过每次要ans = min(ans, b[i])取最小值就行;对于修该两次的情况,首先先按b[i]对数组进行升序排序,提取b[0],也就是最少花费的元素,接着,提取除这个数以外的全部质因数,遍历提取出的全部质因数,通过time = x - (a[idx] % x)算出要使我们选的数产生质因数x,最少要操作几次加1,计算结果每次比较最小值。最终得到答案。
查看代码#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 5; vector<vector<int>> fac(N); void solve(){ int n; cin >> n; vector<int> a(n), b(n); for(auto& x : a) cin >> x; for(auto& x : b) cin >> x; vector<int> org(n); iota(org.begin(), org.end(), 0); sort(org.begin(), org.end(), [&](int x, int y) -> bool{ return b[x] < b[y]; }); int ans = b[org[0]] + b[org[1]]; map<int, int> mp; for(int i = 0; i < n; ++i){ for(auto x : fac[a[i]]){ mp[x]++; if(mp[x] > 1){ ans = 0; } } } for(int i = 0; i < n; ++i){ for(auto x : fac[a[i]]){ mp[x]--; } for(auto x : fac[a[i] + 1]){ if(mp[x] > 0){ ans = min(ans, b[i]); } } for(auto x : fac[a[i]]){ mp[x]++; } } vector<int> check; int idx = org[0]; for(int i = 0; i < n; ++i){ if(i == idx) continue; for(auto x : fac[a[i]]){ check.emplace_back(x); } } for(auto x : check){ int time = x - (a[idx] % x); if(time == x){ time = 0; } ans = min(1ll * ans, 1ll * time * b[idx]); } cout << ans << endl; return ; } int main(){ cin.tie(nullptr)->sync_with_stdio(false); for(int i = 2; i <= N; ++i){ if(!fac[i].empty()) continue; for(int j = i; j <= N; j += i){ fac[j].emplace_back(i); } } int T; cin >> T; while(T--){ solve(); } return 0; }
D. Catshock
我们可以这样想,小猫想从1节点走到n节点,若我们删除1到n中间的节点,很有可能使1到n节点之间的路径消掉,使小猫根本不可能走到n节点,而每次只删除叶子节点,就能保证1到n之间有路径,但如果小猫一开始就在叶子节点上,很有可能就会被删除,那我们可以这样解决,我们可以计算每个节点的深度,当小猫在偶数深度的节点上时,我们只删奇数深度节点上的值,如果奇数深度上没有叶子节点,那让猫再走一步就行,如果还是没有,那就接着走,直至走到n节点为止,题目中对1操作,也就是走一步的操作没有要求,这样做是合法的,但对于操作2及删元素是有要求的,不能连续删两次,那我们这样解,走一步删一步,若无法删,那就接着走,这样做是合法的,也总能走到n节点。
我们以n节点为根,建立树,得到每个节点的连接情况和深度信息,我们执行n-1次操作,每次操作里总有一次删元素,这样最后只会剩下n节点,也就是小猫到达目标。
查看代码void solve(){ int n; cin >> n; int u, v; vector<vector<int>> G(n + 1); for(int i = 1; i < n; ++i){ cin >> u >> v; //u--; v--; G[u].emplace_back(v); G[v].emplace_back(u); } vector<int> dep(n + 1, 0), par(n + 1, 0), ch(n + 1 , 0); auto dfs = [&](auto self, int c, int p) -> void{ dep[c] = dep[p] + 1; par[c] = p; ch[p]++; for(auto x : G[c]){ if(x == p) continue; self(self, x, c); //ch[c]++; } }; dfs(dfs, n, -1); array<vector<int>, 2> leaves; for(int i = 1; i <= n; ++i){ if(ch[i] == 0){ leaves[dep[i] & 1].emplace_back(i); } } vector<array<int, 2>> ans; int col = dep[1] & 1; for(int i = 0; i < n - 1; ++i){ if(leaves[col ^ 1].empty()){ ans.push_back({1, -1}); col ^= 1; } int nxt = leaves[col ^ 1].back(); leaves[col ^ 1].pop_back(); ans.push_back({2, nxt}); int p = par[nxt]; ch[p]--; if(ch[p] == 0){ leaves[dep[p] & 1].push_back(p); } ans.push_back({1, -1}); col ^= 1; } cout << ans.size() << endl; for(auto [x, y] : ans){ if(x == 1){ cout << 1 << endl; }else{ cout << 2 << " " << y << endl; } } }

浙公网安备 33010602011771号