2018年全国多校算法寒假训练营练习比赛(第五场)解题报告

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Tsingcheng_liu/article/details/79377161

A-逆序数

https://www.nowcoder.com/acm/contest/77/A

题目描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。

输入描述:

第一行有一个整数n(1 <= n <= 100000), 然后第二行跟着n个整数,对于第i个数a[i],(0 <= a[i] <= 100000)。

输出描述:

输出这个序列中的逆序数

思路: 堆排列的过程正好可以计算逆序数。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5+10;
int arr[maxn], tmp[maxn];
long long ans;

void merge(int *a, int st, int mid, int ed) {
    int i = st, j = mid + 1, k = st;
    while (i <= mid && j <= ed) {
        if (a[i] <= a[j]) {
            tmp[k++] = a[i++];
        } else {
            ans += j - k;
            tmp[k++] = a[j++];
        }
    }

    while (i <= mid) {
        tmp[k++] = a[i++];
    }
    while (j <= ed) {
        tmp[k++] = a[j++];
    }
    for (int i = st; i <= ed; i++) {
        a[i] = tmp[i];
    }
}

void merge_sort(int *a, int st, int ed) {
    if (st < ed) {
        int mid = (st+ed) / 2;
        merge_sort(a, st, mid);
        merge_sort(a, mid+1, ed);
        merge(a, st, mid, ed);
    }
}

int main(void) {
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    merge_sort(arr, 0, n-1);
    cout << ans << endl;
} 

B-Big Water Problem

题目描述

给一个数列,会有多次询问,对于每一次询问,会有两种操作:
1:给定两个整数x, y, 然后在原数组的第x位置上加y;
2:给定两个整数l,r,然后输出数组从第l位加到第r位数字的和并换行

输入描述:

第一行有两个整数n, m(1 <= n, m <= 100000)代表数列的长度和询问的次数
第二行n个数字,对于第i个数字a[i],(0<=a[i]<=100000)。
接下来m行,每一行有三个整数f, x, y。第一个整数f是1或者是2,代表操作类型,如果是1,接下来两个数x,y代表第x的位置上加y,如果是2,则求x到y的和,保证数据合法。

输出描述:

输出每次求和的结果并换行

思路: 线段树单点更新模板题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
int tree[maxn<<2];

inline void pushUp(int node) {
    tree[node] = tree[node<<1] + tree[node<<1|1];
}

void build(int node, int l, int r) {
    if (l == r) cin >> tree[node];
    else {
        build(node<<1, l, (l+r) / 2);
        build((node<<1) +1, (l+r) / 2 + 1, r);

        pushUp(node);
    }    
}


void updata(int l, int r, int node, int index, int num) {
    if (l == r) {
        tree[node] += num;
        return;
    }
    int mid = (l+r) >> 1;
    if (index <= mid)  updata(l, mid, node << 1, index, num);
    else updata(mid+1, r, (node << 1)+1, index, num);
    pushUp(node);
}

int query(int node, int l, int r, int i, int j) {
   if (i <= l && j >= r) return tree[node];
   int mid = (l+r) >> 1, sum = 0;
   if (i <= mid) sum += query(node<<1, l, mid, i, j);
   if (j > mid) sum += query(node<<1|1, mid+1, r, i, j);
   return sum;
}

int main(void) {
    ios::sync_with_stdio(0);

    int n, m;
    cin >> n >> m;

    build(1, 1, n);
    while (m--) {
        int a, b, op;
        cin >> op >> a >> b;
        if (op == 2) 
            cout << query(1, 1, n, a, b) << endl;
        else
            updata(1, n, 1, a, b);
    }
    return 0;
}

C-字符串的问题

题目描述:

有一个字符串 让你找到这个字符串 S 里面的子串T 这个子串 T 必须满足即使这个串的前缀 也是这个
串的后缀 并且 在字符串中也出现过一次的(提示 要求满足前后缀的同时也要在字符串中出现一次 只是前后缀可不行 输出最长满足要求字符串)

输入描述:

给出一个字符串 长度 1 到 1e6 全部是小写字母

输出描述:

如果找的到就输出这个子串T 如果不行就输出 Just a legend

思路: 用string可以莽过

#include <bits/stdc++.h>
using namespace std;

string s; 
string pre, suf, mid, ans = "Just a legend";
int main(void) {
    cin >> s;
    int len = s.size();
    if (len < 3) {
        puts("Just a legend");
        return 0;
    }
    int k = 0;
    while (++k < len) {
        pre = s.substr(0, k);
        suf = s.substr(len-k, k);
        if (pre == suf) {
            mid = s.substr(1, len-2);
            if (mid.find(pre) < len-1) ans = pre;
        }
    }
    cout << ans << endl;
}

D-集合问题

题目描述

给你a,b和n个数p[i],问你如何分配这n个数给A,B集合,并且满足:
若x在集合A中,则a-x必须也在集合A中。
若x在集合B中,则b-x必须也在集合B中。

输入描述:

第一行 三个数 n a b 1<=n<=1e5 1<=a,b<=1e9
第二行 n个数 p1 p2 p3…pn 1<=pi<=1e9

输出描述:

如果可以恰好分开就输出第一行 YES
然后第二行输出 n个数 分别代表pi 是哪个集合的 0 代表是A集合 1代表是B 集合
不行就输出NO
放在哪个集合都可以的时候优先放B

思路:暴力二分可以过, 正解好是并查集(

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int arr[maxn], pos[maxn];

int main(void) {
    int n, a, b;
    cin >> n >> a >> b;
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    sort(arr, arr+n);
    bool flag = 0;
    for (int i = 0; i < n; i++) {
        if (!pos[i]) {
            int x = lower_bound(arr, arr+n, a-arr[i]) - arr;
            int y = lower_bound(arr, arr+n, b-arr[i]) - arr;
            if (!pos[y] && arr[y] + arr[i] == b) {
                pos[y] = pos[i] = 1;
                continue;
            }
            if (!pos[x] && arr[x] + arr[i] == a) {
                pos[x] = pos[i] = 2;
                continue;
            }
            flag = 1;
            break;
        }
    }
    if (flag) puts("NO");
    else {
        puts("YES");
        printf("%d", pos[0]&1);
        for (int i = 1; i < n; i++) {
            printf(" %d", pos[i]&1);
        }
        puts("");
    }
}

E-情人节的电灯泡

题目描述

情人节到了,小芳和小明手牵手,打算过一个完美的情人节,但是小刚偏偏也来了,当了一个明晃晃的电灯泡,小明很尴尬,就和小刚说,我交给你个任务,你完成了我俩就带你玩,否则你就回家吧。小刚很有当单身狗的觉悟,他坚决不想让小明过好情人节,同为单身狗的你能帮帮他吗?现在有一个n×n(1 <= n <= 1000)的格子,每一个格子都有一个电灯泡,可能是亮的,也可能是灭的(1代表亮, 0代表灭),现在有两种操作,一种是给你一个坐标,对于那个坐标上的灯泡,如果他是亮的,那么熄灭他,反之如果他是灭的,那么打开它。第二种操作是给你两个坐标,第一个坐标代表一个子矩阵的左上角,另一个坐标是右下角,请你求出当前子矩阵中有多少个灯泡是亮着的。燥起来吧!!!单身狗们!!!!

输入描述:

第一行两个整数,n(1 <= n <= 1000)和m(1 <= m <= 100000),分别代表正方形格子的边长和询问次数。
接下来n行,每一行有n个bool形数字(0或1),代表灯泡的状态。
接下来m行,每一行第一个数字f(1或2)代表操作的类型,如果f是1,那么接下来输入一个坐标(x, y)(1 <= x, y <= n),对于当前位置的灯泡状态进行改变,如果是2,那么接下来输入两个坐标(x1, y1)(1 <= x1, y1 <= n), (x2, y2)(1 <= x2, y2 <= n),确定子矩阵的位置,输出子矩阵中亮着的灯泡数量并换行。

输出描述:

对于每一个2操作,输出子矩阵中亮着的灯泡数量并换行。

思路:模拟一下操作就ok了

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
bool arr[maxn][maxn];

int main(void) {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            scanf("%d", &arr[i][j]);
        }
    }

    while (m--) {
        int op;
        scanf("%d", &op);
        if (op == 1) {
            int x, y;
            scanf("%d %d", &x, &y);
            arr[x][y] = !arr[x][y];
        } else {
            int x1, y1, x2, y2;
            scanf("%d %d %d %d", &x1, &y1, &x2, &y2);

            long long ans = 0;
            for (int i = x1; i <= x2; i++) {
                for (int j = y1; j <= y2; j++) {
                    if (arr[i][j]) ans++;
                }
            } 
            printf("%lld\n", ans);
        }
    }
    return 0;
} 

F-The Biggest Water Problem

题目描述

给你一个数,让他进行巴啦啦能量,沙鲁沙鲁,小魔仙大变身,如果进行变身的数不满足条件的话,就继续让他变身。。。直到满足条件为止。
巴啦啦能量,沙鲁沙鲁,小魔仙大变身:对于一个数,把他所有位上的数字进行加和,得到新的数。
如果这个数字是个位数的话,那么他就满足条件。

输入描述:

给一个整数数字n(1<=n<=1e9)。

输出描述:

输出由n经过操作满足条件的数

思路:依旧是模拟

#include <bits/stdc++.h>
using namespace std;

int main(void) {
    int n, ans;
    cin >> n;
    while (n > 10) {
        ans = 0;
        while (n) {
            ans += n % 10;
            n /= 10;
        }
        n = ans;
    }
    cout << ans << endl;
}

G-送分啦-QAQ

题目描述

儿园开学了,为了让小盆友们能尽可能的多的享受假期。校长大人决定让小盆友分批到校,至于每批学生来多少人由一个小傻子和一个小仙女负责,两个人轮番负责,校长会在最后的时候去查看工作进度,小傻子不想被别人嘲笑自己傻,小仙女要证明自己比小傻子聪明。所以她们回去争抢安排最后一名小盆友。每次安排的小盆友至少为1,至多为上一次安排的2倍。小仙女抢到了先手的机会。第一次安排小盆友不能直接安排所有的小盆友一起回校。

输入描述:

单组测试数据
输入一个整数n——n代表小盆的个数(n>=2&&n<=1e9)

输出描述:

输出获胜人的名字——“Xian”或者“Sha”

思路: 斐波那契博弈模型: 有一堆个数为n的石子,游戏双方轮流取石子,满足:
1)先手不能在第一次把所有的石子取完;
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间(包含1和对手刚取的石子数的2倍)。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4+10;
#define ll long long
ll fib[maxn];

int main(void) {
    fib[0] = fib[1] = 1;
    for (int i = 2; i < maxn; i++) {
        fib[i] = fib[i-1] + fib[i-2];
    }

    int n;
    cin >> n;
    for (int i = 2; i < maxn; i++) {
        if (fib[i] == n) {
            puts("Sha");
            return 0;
        }
    }
    puts("Xian");
}

H-Tree Recovery

题目描述

You have N integers, A1, A2, … , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

输入描述:

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, … , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
“C a b c” means adding c to each of Aa, Aa+1, … , Ab. -10000 ≤ c ≤ 10000.
“Q a b” means querying the sum of Aa, Aa+1, … , Ab.

输出描述:

You need to answer all Q commands in order. One answer in a line.

思路: 线段树区间更新&区间查询模板题

#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define ll long long
const int N = 1e5+10;
ll add[N<<2], sum[N<<2];

void pushUp(int rt) {
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void pushDown(int rt, int ln, int rn) {
    if (add[rt]) {
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        sum[rt<<1] += (ll)ln*add[rt];
        sum[rt<<1|1] += (ll)rn*add[rt];
        add[rt] = 0 ;
    }
}

void build(int l, int r, int rt) {
    add[rt] = 0;
    if (l == r) {
        cin >> sum[rt];
        return;
    }
    int m = (l+r) >> 1;
    build(lson);
    build(rson);
    pushUp(rt);
}

void updata(int L, int R, ll C, int l, int r, int rt) {
    if (L <= l && r <= R) {
        sum[rt] += C*(r-l+1);
        add[rt] += C;
        return;
    }
    int m = (l+r) >> 1;
    pushDown(rt, m-l+1, r-m);

    if (L <= m) updata(L, R, C, lson);
    if (R > m) updata(L, R, C, rson);
    pushUp(rt);
    return;
}

ll query(int L, int R, int l, int r, int rt) {
    if (L <= l && r <= R) return sum[rt];
    int m = (l+r) >> 1;
    pushDown(rt, m-l+1, r-m);

    ll ans = 0;
    if (L <= m) ans += query(L, R, lson);
    if (R > m) ans += query(L, R, rson);
    return ans;
}


int main(void) {
    ios::sync_with_stdio(0); cin.tie(0);
    int n, m;
    while (cin >> n >> m) {
        build(1, n, 1);
        char ch;
        while (m--) {
            cin >> ch;
            int L, R;
            if (ch == 'Q') {
                cin >> L >> R;
                cout << query(L, R, 1, n, 1) << endl;
            } else {
                ll C;
                cin >> L >> R >> C;
                updata(L, R, C, 1, n, 1);
            }
        }
    }
    return 0;
}

                               ——EDN

展开阅读全文

没有更多推荐了,返回首页