CF刷题:2020:04:27: ~ 2020:05:03 (期间日更)
Posted lifehappy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF刷题:2020:04:27: ~ 2020:05:03 (期间日更)相关的知识,希望对你有一定的参考价值。
1、Constant Palindrome Sum
思路
由于每一个数字的取值范围是([1, k]),所以对与每一对数字的和的取值应该在([2, 2k])。
对于每一对数字我们可以选择改变一个、改变两个或者一个都不改变。
-
改变一个的时候,数值区域可以变成([min(a, b) + 1, max(a, b) + k])
-
改变两个的时候,数值区域可以变成([2, min(a, b)]),和([max(a, b) + k + 1, 2k])。
-
一个都不变,其值只有可能是(a + b)。
我们定义三个数组
-
1、num数组,记录的是下标为 ((a + b))的数对的个数。
-
2、change1数组,记录的是改变一个数字的时候可取的区域相应的操作数,这里为了降低复杂度所以采用差分的形式,大致操作如下
(change1[min(a, b) + 1]++)
(change1[max(a, b) + k + 1]--) -
3、change2数组,记录的是改变两个数字对应的操作次数,其对应对change2数组的操作如下
(change2[2] += 2,change2[min(a, b) + 1] -= 2)
(change2[max(a, b) + k + 1] += 2,change2[2k + 1] -= 2)
所以最后的答案就是当前答案操作一次的数量加上操作两次的数量减去当前答案的数值的数量
也就是 (change1[i] + change2[i] - num[i])
减去 (num[i]) 的操作相信大家应该明白,就是在
([min(a, b) + 1, max(a, b) + k]) 中包括了 (a + b)。
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 4e5 + 10;
const int INF = 0x3f3f3f3f;
int a[N], num[N], change1[N], change2[N], n, m;
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 0; i <= 2 * m + 5; i++)
num[i] = change1[i] = change2[i] = 0;
//所有和的可能性[2, 2m]
for(int i = 1; i <= n / 2; i++) {
num[a[i] + a[n - i + 1]]++;
int l = min(a[i], a[n - i + 1]) + 1;
int r = max(a[i], a[n - i + 1]) + m;
//[l, r]只要改变一个数
change1[l]++, change1[r + 1]--;
//[2, l - 1] 和 [r + 1, 2m]改变两个数。
change2[2] += 2, change2[l]-= 2;
change2[r + 1] += 2;
}
for(int i = 3; i <= 2 * m + 5; i++) {
// num[i] += num[i - 1];
change1[i] += change1[i - 1];
change2[i] += change2[i - 1];
}
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", num[i]);
// }
// puts("");
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", change1[i]);
// }
// puts("");
// for(int i = 2; i <= 2 * m; i++) {
// printf("%d ", change2[i]);
// }
// puts("");
int ans = INF;
for(int i = 2; i <= 2 * m; i++)
ans = min(ans, change1[i] - num[i] + change2[i]);
printf("%d
", ans);
}
return 0;
}
2、Edge Weight Assignment
思路
代码
3、Xenia and Colorful Gems
思路
总共有三组数,要求 ((x?y)^2+(y?z)^2+(z?x)^2) 最小值,我们不妨设 a 在第一组数里面,b 在第二组数里面,c 在第三组数里面。
共有如下几种情况
- (a <= b <= c)
- (a <= c <= b)
- (b <= a <= c)
- (b <= c <= a)
- (c <= a <= b)
- (c <= b <= a)
我们考虑枚举中间的数,然后让两边的数趋近于它,这样最后得到的结果就一定满足条件。
这里用lowered_bound来确定,大于等于的数。
用upper_bound返回的指针(减一)来确定小于等于的数。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
long long get(ll x, ll y, ll z) {
return (x - y) * (x - y) + (y - z) * (y - z) + (z - x) * (z - x);
}
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
vector<int> a[3];
int n[3];
for(int i = 0; i < 3; i++) {
scanf("%d", &n[i]);
a[i].resize(n[i]);
}
for(int i = 0; i < 3; i++) {
for(int j = 0; j < n[i]; j++)
scanf("%d", &a[i][j]);
sort(a[i].begin(), a[i].end());
}
ll ans = 0x3f3f3f3f3f3f3f3f;
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
for(int k = 0; k < 3; k++) {//保证j <= i <= k;
if(i == j || i == k || j == k) continue;
for(vector<int> :: iterator p1 = a[i].begin(); p1 != a[i].end(); p1++) {
auto p2 = lower_bound(a[j].begin(), a[j].end(), *p1);
auto p3 = upper_bound(a[k].begin(), a[k].end(), *p1);
p3--;
if(p3 >= a[k].begin() && p2 < a[j].end())
ans = min(ans, get(*p1, *p2, *p3));
}
}
printf("%lld
", ans);
}
return 0;
}
4、Linova and Kingdom
思路
我们可以知道,如果把一座城市当成工业城市,它会影响其子树上的点,我们定义两个数组,一个dis代表当前节点到根节点1的距离,sz数组代表,当前节点的子树的节点数量。
通过贪心,也就是得到 (dis - sz) 的前 (k) 项。
再贪心之前我们必须得明白,选址工业城市一定是从外向内的,所以上面的贪心是一定成立的,当选了一个节点作为工业城市时,其子树上的所有节点一定也必须是工业城市了。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N1 = 2e5 + 10, N2 = 4e5 + 10;
int head[N1], to[N2], nex[N2], cnt = 1;
int dis[N1], sz[N1], n, m;
void add(int x, int y) {
to[cnt] = y;
nex[cnt] = head[x];
head[x] = cnt++;
}
void dfs(int now, int fa) {
dis[now] = dis[fa] + 1;
sz[now] = 1;
for(int i = head[now]; i; i = nex[i]) {
// cout << to[i] << endl;
if(to[i] != fa) {
dfs(to[i], now);
sz[now] += sz[to[i]];
}
}
dis[now] -= sz[now];
}
int main() {
// freopen("in.txt", "r", stdin);
int x, y;
scanf("%d %d",&n, &m);
for(int i = 1; i < n; i++) {
scanf("%d %d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1, 0);
sort(dis + 1, dis + n + 1, greater<int> ());
ll ans = 0;
for(int i = 1; i <= m; i++)
ans += dis[i];
printf("%lld
", ans);
return 0;
}
5、Three Blocks Palindrome (easy version)
Three Blocks Palindrome (easy version) 题目链接
思路
代码
6、Minimum Euler Cycle
思路
找到一个完全图遍历的最小字典序中的 l ~ r 的位置。我们可以发现最小字典序显然如下
1 2 1 3 1 4 1 5 ………… 1 (n - 1) 1 n
2 3 2 4 2 5 ………… 2 (n - 1) 2 n
……………………………………………………………………
…………………………………………………………
………………………………………………
……………………………………
…………………………
(n - 1) n
1
一共有 (n(n - 1) + 1) 个数字,最后一行比较特殊,只有一个 1
因此我们只需要用循环来枚举这些数就行,最后一个数特殊判断处理。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, l, r, sum;
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
scanf("%lld %lld %lld", &n, &l, &r);
// cout << n << " " << l << " " << r << endl;
sum = 1;
for(ll i = 1; i < n; i++) {
if(sum + 2 * (n - i) <= l) {
sum = sum + 2 * (n - i);
continue;
}
// cout << i << endl;
for(int j = i + 1; j <= n; j++) {
if(sum >= l && sum <= r)
printf("%d ", i);
sum++;
if(sum >= l && sum <= r)
printf("%d ", j);
sum++;
}
if(sum > r) break;
}
// cout << sum << endl;
if(sum <= r) puts("1");
else printf("
");
}
return 0;
}
7、Circle of Monsters
思路
先是把所有伤害互传造成的结果记录,然后再通过简单的枚举初始位置来找最小值,整体复杂度是 (O(n)) 的,可以说是一道水题,主要思想应该就是贪心吧。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e6 + 10;
ll a[N], b[N], cost[N];
int n;
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
ll sum = 0;
scanf("%d", &n);
for(int i = 0; i < n; i++) {
scanf("%lld %lld", &a[i], &b[i]);//注意cost[i]不可能小于0所以这里要特判一下。
if(i) cost[i] = max((ll)0, a[i] - b[i - 1]), sum += cost[i];
}
cost[0] = max((ll)0, a[0] - b[n - 1]);
sum += cost[0];
ll ans = sum - cost[0] + a[0];//取位置0作为起始位置,去找最优的起始位置。
for(int i = 0; i < n; i++)
ans = min(ans, sum - cost[i] + a[i]);
printf("%lld
", ans);
}
return 0;
}
8、Eugene and an array
思路
代码
9、Walk on Matrix
思路
代码
10、Dreamoon Likes Coloring
思路
代码
11、Carousel
思路
代码
12、Count The Blocks
思路
代码
13、Game with Chips
思路
一开始以为还要用bfs或者dfs来写,然后仔细想一想,div2的C题应该没这么复杂吧。果然,这就是一道脑洞题,我们发现题目给的数据是步数小于等于 (2nm) 这足够走两遍完整的地图了,于是有了下面的做法。
我们把所有的点都移到四个角中其中任意一个,现在所有的点都在同一个角上了,所以我们只需要用一个蛇形来遍历整个地图就行了,总的步数是((n + m - 1) + (n * m - 1) < (n * m))
代码
这里采用的做法是把所有的点都移到左上角。
#include<bits/stdc++.h>
using namespace std;
int main() {
// freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
int n, m, k, x, y;
cin >> n >> m >> k;
for(int i = 0; i < k; i++)
cin >> x >> y;
for(int i = 0; i < k; i++)
cin >> x >> y;
cout << ((n + m - 2) + (n * m - 1)) << endl;
for(int i = 1; i < n; i++) cout << "U";
for(int i = 1; i < m; i++) cout << "L";
for(int i = 1; i <= n; i++) {
if(i != 1) cout << "D";
for(int j = 1; j < m; j++)
if(i & 1) cout << "R";
else cout << "L";
}
cout << endl;
return 0;
}
14、Ehab the Xorcist
思路
代码
15、Two Arrays
思路
代码
16、Standard Free2play
思路
代码
17、p-binary
思路
代码
18、Welfare State
思路
代码
19、Prime Graph
思路
代码
20、Almost All Divisors
思路
这题稍微简单,直接模拟,我们知道 (x) 一定是排序后的 (a[1] * a[n])
所以直接排序,然后再找一遍 (x) 的因子,最后比对这两个因子数组是不是一样的,一样就输出 (x),否者就输出 (-1)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e3 + 10;
ll a[N], b[N];
int n, m;
int main() {
// freopen("in.txt", "r", stdin);
int t;
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lld", &a[i]);
sort(a, a + n);
ll ans = a[0] * a[n - 1];
m = 0;
for(int i = 2; 1ll * i * i <= ans; i++) {
if(ans % i == 0) {
b[m++] = i;
if(ans / i != i)
b[m++] = ans / i;
}
}
if(n != m) {
puts("-1");
continue;
}
sort(b, b + m);
int flag = 1;
for(int i = 0; i < n; i++)
if(a[i] != b[i]) {
flag = 0;
break;
}
if(flag) printf("%lld
", ans);
else puts("-1");
}
return 0;
}
以上是关于CF刷题:2020:04:27: ~ 2020:05:03 (期间日更)的主要内容,如果未能解决你的问题,请参考以下文章