#牛客网 2018年牛客多校算法寒假训练营练习比赛(第五场)

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)。

输出描述:

输出这个序列中的逆序数

示例1

输入

 

5
4 5 1 3 2

输出

 

7

思路 : 直接暴力就行

AC代码:

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;

int p[maxn], n;
ll x, sum;

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x;
        sum += p[x];
        for (int j = 1; j < x; j++) p[j]++;
    }
    cout << sum << endl;
    return 0;
}

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的和,保证数据合法。

输出描述:

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

示例1

输入

 

10 2
1 2 3 4 5 6 7 8 9 10
1 1 9
2 1 10

输出

 

64

思路 :差分数组

AC代码 :

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;

ll p[maxn], s[maxn], n, m, wi, ui, vi;

int main()
{
    scanf("%lld%lld", &n, &m);
    for (ll i = 1; i <= n; i++) {
        scanf("%lld", &s[i]);
        p[i] = s[i] + p[i - 1];
    }
    while (m--) {
        scanf("%lld", &wi);
        if (wi == 1) {
            scanf("%lld%lld", &ui, &vi);
            for (int j = ui; j <= n; j++) p[j] += vi;
        }
        else {
            scanf("%lld%lld", &ui, &vi);
            cout << p[vi] - p[ui - 1] << endl;
        }
    }
    return 0;
}

 

 C-字符串的问题

 

题目描述

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

输入描述:

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

输出描述:

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

示例1

输入

 

fixprefixsuffix

输出

 

fix

示例2

输入

 

abcdabc

输出

 

Just a legend

题目大意 : 有一个字符串, 如果他的前缀和后缀相等且该子串包含在字符串中间 (去掉头尾和)那么输出这个字符串,入股哦有多个,输出最长的那个

思路 :也是模拟,从前往后每次多加一个长度,看前缀后缀是否相等。然后在判断该子串是否包含在内,是就存下来,最后排个序输出最长的

AC代码 :

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;

char c[maxn], s[maxn], t[maxn];
int ans = 1, flag, X, C, S;
string p[maxn];
string line, ch, str;
bool cmp (string a, string b) {
    return a.size() > b.size();
}

int main()
{
    cin >> line;
    for (int i = 1; i < line.size() - 1; i++) c[X++] = line[i];
    while (ans < line.size()) {
        ch = line.substr(0, ans), str = line.substr(line.size() - ans, line.size() - 1);
        if (ch == str) {   
            C = 0;
            for (int i = 0; i < ch.size(); i++) s[C++] = ch[i];
            if (strstr(c, s) != NULL || strcmp(c, s) == 0) {flag = 1; p[S++] = ch;}
        }
        ans++;
    }
    if (!flag) cout << "Just a legend" << endl;
    else {
        sort (p, p + S, cmp);
        cout << p[0] << endl;
    }
    return 0;
}

 

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

示例1

输入

 

4 5 9
2 3 4 5

输出

 

YES
0 0 1 1

示例2

输入

 

3 3 4
1 2 4

输出

 

NO

 题目大意 :如果一个数在集合中,那么a - 该数或者 b - 该数必须也在集合中,如果输入的数符合该条件,输出集合分配情况

思路 : 二分模拟查找,因为A和B集合都可以存的情况下优先放B,所以我们直接先判断B集合是否可以存,如果不行,看A集合是否可以,如果还不行,直接break不成立

AC代码 :

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;

ll p[maxn], n, ai, bi, u;
ll s[maxn], flag = 1;
bool vis[maxn];
ll lower(int m) {
    ll l = 0, r = n - 1, mid;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (p[mid] > m) r = mid - 1;
        else l = mid + 1;
        if (p[mid] == m && !vis[mid]) return mid;
    }
    return n;
}

int main()
{
    cin >> n >> ai >> bi;
    for (int i = 0; i < n; i++) cin >> p[i];
    sort (p, p + n);
    for (int i = 0; i < n; i++) {
        if (vis[i]) continue;  // 没有查过
        u = lower(bi - p[i]);  
        if (u != n && p[u] + p[i] == bi) // 找到了
            s[u] = s[i] = 1, vis[u] = vis[i] = 1;  // 标记并存进B
        else {
            u = lower(ai - p[i]);
            if (u != n && p[u] + p[i] == ai)
                s[u] = s[i] = 0, vis[u] = vis[i] = 1;  // 标记并存A
            else {flag = 0; break;}  // A和B都不可存,直接跳出
        }
    }
    if (!flag) cout << "NO" << endl;
    else {
        cout << "YES" << endl;
        for (int i = 0; i < n - 1; i++) cout << s[i] << " ";
        cout << s[n - 1] << endl;
    }
    return 0;
}

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操作,输出子矩阵中亮着的灯泡数量并换行。

示例1

输入

 

6 4
0 0 1 0 1 0
1 0 1 1 0 1
1 0 0 0 0 0
1 1 0 0 1 0
0 0 0 0 1 1
0 0 0 0 1 0
2 2 2 4 5
1 1 1
2 1 1 6 6
1 2 6

输出

 

4
14

思路 : 水题,直接模拟不会超时

AC代码 :

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;

bool p[maxn][maxn];
int n, m, ui, vi, u, v, wi;

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) cin >> p[i][j];
    }
    while (m--) {
        cin >> wi;
        if (wi == 1) {
            cin >> ui >> vi;
            p[ui][vi] = !p[ui][vi];
        }
        else {
            int ans = 0;
            cin >> ui >> vi >> u >> v;
            for (int i = ui; i <= u; i++) {
                for (int j = vi; j <= v; j++) {
                    if (p[i][j]) ans++;
                }
            }
            cout << ans << endl;
        }
    }
    return 0;
}

 F-The Biggest Water Problem

 

题目描述

给你一个数,让他进行巴啦啦能量,沙鲁沙鲁,小魔仙大变身,如果进行变身的数不满足条件的话,就继续让他变身。。。直到满足条件为止。

巴啦啦能量,沙鲁沙鲁,小魔仙大变身:对于一个数,把他所有位上的数字进行加和,得到新的数。

如果这个数字是个位数的话,那么他就满足条件。

输入描述:

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

输出描述:

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

示例1

输入

 

12

输出

 

3

说明

12 -> 1 + 2 = 3

示例2

输入

 

38

输出

 

2

说明

38 -> 3 + 8 = 11 -> 1 + 1 = 2

思路 :把每一位表示出来直到个位

AC代码 :

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

int main()
{
    ll n;
    cin >> n;
    while (n >= 10) {
        ll sum = 0, temp = n;
        while (temp) {
            sum += temp % 10;
            temp /= 10;
        }
        n = sum;
    }
    cout << n << endl;
    return 0;
}

G-送分啦QAQ

 

题目描述

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

输入描述:

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

输出描述:

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

示例1

输入

 

3

输出

 

Sha

说明

(Fisrt)1 -> (Second)  2         ||     2 - > 1  无论小仙女先送一个还是两个都会被小傻子获胜

示例2

输入

 

4

输出

 

Xian

说明

1 -> 2 -> 1    ||    1 -> 1 -> 2        小仙女先送一个,小傻子无论送一个或者两个都会被小仙女取胜。

 

 题目大意 :每次最少取一个,最多取上一次的2倍,小仙女先取,输入石子数量,输出获胜者

思路 : 有一个规律,满足斐波那契的石子,一定是谁先拿谁输

AC代码 :

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 50 + 5;

ll p[maxn], n, ans;

int main()
{
    p[0] = 2, p[1] = 3;
    for (int i = 2; i < 50; i++) p[i] = p[i - 1] + p[i - 2];
    cin >> n;
    ans = lower_bound (p, p + 50, n) - p;
    if (p[ans] != n) cout << "Xian" << endl;
    else cout << "Sha" << endl;
    return 0;
}

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.

示例1

输入

 

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

输出

 

4
55
9
15

题目大意 : 输入一个序列和查询次数,Q表示输出该区间的和,C表示该区间的数每个数 + 第三个数

思路 :线段树模板题

AC代码 :

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 5;

struct node
{
    ll l, r, lazy, len, w;  // l表示左边界,r表示右边界,lazy用来更改区间,len表示区间大小
}tree[maxn * 4];
ll p[maxn], n, m;
char op;
void build(ll root, ll l, ll r) {  // 建树
    tree[root].l = l;
    tree[root].r = r;
    tree[root].lazy = 0;
    tree[root].len = r - l + 1;
    if (l == r) {
        tree[root].w = p[l];
        return ;
    }
    ll mid = (l + r) >> 1;
    build(root << 1, l, mid);
    build(root << 1 | 1, mid + 1, r);
    tree[root].w = tree[root << 1].w + tree[root << 1 | 1].w;
}
void pushdown(ll root) {  // 回溯
    tree[root << 1].lazy += tree[root].lazy;
    tree[root << 1 | 1].lazy += tree[root].lazy;
    tree[root << 1].w += tree[root << 1].len * tree[root].lazy;
    tree[root << 1 | 1].w += tree[root << 1 | 1].len * tree[root].lazy;
    tree[root].lazy = 0;
}
void update(ll root, ll l, ll r, ll y) {  // 修改区间
    if (tree[root].l >= l && tree[root].r <= r) {
        tree[root].lazy += y;
        tree[root].w += tree[root].len * y;
        return;
    }
    if (tree[root].l > r || tree[root].r < l)
        return;
    if (tree[root].lazy) pushdown(root);
    update(root << 1, l, r, y);
    update(root << 1 | 1, l, r, y);
    tree[root].w = tree[root << 1].w + tree[root << 1 | 1].w;
}
ll query(ll root, ll l, ll r) {   // 区间和
    if (tree[root].l >= l && tree[root].r <= r)
        return tree[root].w;
    if (tree[root].l > r || tree[root].r < l)
        return 0;
    if (tree[root].lazy) pushdown(root);
    return query(root << 1, l, r) + query(root << 1 | 1, l, r);
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> p[i];
    build(1, 1, n);
    while (m--) {
        int ui, vi, wi;
        cin >> op;
        if (op == 'Q') {
            cin >> ui >> vi;
            cout << query(1, ui, vi) << endl;
        }
        else {
            cin >> ui >> vi >> wi;
            update(1, ui, vi, wi);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值