CF刷题:2020:04:27: ~ 2020:05:03 (期间日更)

1、Constant Palindrome Sum

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\n", ans);
    }
    return 0;
}

2、Edge Weight Assignment

Edge Weight Assignment 题目链接

思路

代码

3、Xenia and Colorful Gems

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\n", ans);
    }
    return 0;
}

4、Linova and Kingdom

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\n", ans);
    return 0;
}

5、Three Blocks Palindrome (easy version)

Three Blocks Palindrome (easy version) 题目链接

思路

代码

6、Minimum Euler Cycle

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("\n");
    }
    return 0;
}

7、Circle of Monsters

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\n", ans);
    }
    return 0;
}

8、Eugene and an array

Eugene and an array 题目链接

思路

代码

9、Walk on Matrix

Walk on Matrix 题目链接

思路

代码

10、Dreamoon Likes Coloring

Dreamoon Likes Coloring 题目链接

思路

代码

11、Carousel

Carousel 题目链接

思路

代码

12、Count The Blocks

Count The Blocks 题目链接

思路

代码

13、Game with Chips

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

Ehab the Xorcist 题目链接

思路

代码

15、Two Arrays

Two Arrays 题目链接

思路

代码

16、Standard Free2play

Standard Free2play 题目链接

思路

代码

17、p-binary

p-binary 题目链接

思路

代码

18、Welfare State

Welfare State 题目链接

思路

代码

19、Prime Graph

Prime Graph 题目链接

思路

代码

20、Almost All Divisors

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\n", ans);
        else    puts("-1");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/lifehappy/p/12790295.html