本文介绍: 给出一个长度为n的数组a,每次可以选择一个长度不超过k的子数组进行”翻转操作,问能否将该数排序成非降序ijaiaj​。

A.Halloumi Boxes思维

题意:

给出一个长度

n

n

n数组

a

a

a,每次可以选择一个长度不超过

k

k

k的子数组进行”翻转操作,问能否将该数组排序成非降序

分析

如果数组元素本身就是非降序(不严格递增),那么可以完成。

如果数组元素降序的,那么当

k

2

k ge 2

k2时,就可以模拟冒泡排序过程,此时一定可以完成排序,而如果

k

=

1

k = 1

k=1,无法改变任何元素位置,无法完成排序。

代码

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e6 + 5;

int a[N];

void solve() {
    int n, k;
    cin >> n >> k;
    int flag = 1;
    for (int i = 0; i < n; i++) {
        cin >> a[i];
        if (i > 0 &amp;&amp; a[i] < a[i - 1]) {
            flag = 0;
        }
    }
    if (flag == 0 &amp;&amp; k <= 1) {
        cout << "NO" << endl;
    } else {
        cout << "YES" << endl;
    }
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

B.StORage room(位运算

题意:

给出一个

n

×

n

n times n

n×n矩阵

M

M

M矩阵

M

M

M中第

i

i

i行第

j

j

j列的元素表示

a

i

a

j

a_i | a_j

aiaj结果,问能否找出矩阵对应

a

a

a数组

如果有多种答案输出任意一个即可

分析

既然

M

i

,

j

M_{i,j}

Mi,j表示

a

i

a

j

a_i | a_j

aiaj,且只要找到一组合法的答案即可,那么让

a

i

a_i

ai就等于

M

i

,

1

&amp;

M

i

,

2

&amp;

.

.

.

&amp;

M

i

,

n

M_{i, 1} &amp; M_{i, 2} &amp; … &amp; M_{i, n}

Mi,1&amp;Mi,2&amp;…&amp;Mi,n,再检查生成

a

a

a数组是否满足

M

M

M矩阵即可。

代码

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e3 + 5;

int a[N][N], ans[N];

void solve() {
    memset(ans, -1, sizeof(ans));
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            cin >> a[i][j];
            if (i != j) {
                if (ans[i] == -1) {
                    ans[i] = a[i][j];
                } else {
                    ans[i] &amp;= a[i][j];
                }
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i != j && ((ans[i] | ans[j]) != a[i][j])) {
                cout << "NO" << endl;
                return;
            }
        }
    }
    cout << "YES" << endl;
    for (int i = 1; i <= n; i++) {
        cout << max(0, ans[i]) << ' ';
    }
    cout << endl;
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

C.Theofanis’ Nightmare(贪心)

题意:

给出一个包

n

n

n个元素的数组

a

a

a,你可以任意该数划分成若干个子段,然后统计划分完的结果为:

  • i

    =

    1

    k

    i

    s

    u

    m

    i

    sumlimits_{i = 1}^{k}i cdot sum_i

    i=1kisumi(

    k

    k

    k为子段数量,

    s

    u

    m

    i

    sum_i

    sumi为第

    i

    i

    i个子段的数字总和)

问划分的结果最大是多少。

分析

考虑每个子段的数字贡献,

i

s

u

m

i

i cdot sum_i

isumi也可以转化为该子段中每个数字被加上了

i

i

i次。那么就可以把乘法转化为加法

然后考虑怎么划分子段最优,如果从前往后考虑,很难判断是否该将当前数字划分为一个新子段的起点。

因此,需要从后往前遍历统计

i

n

i sim n

in之间的数字总和,为了使答案尽可能大,那么只要当前数字总和大于等于0,就可以将当前位置划分给一个新的子段起点。此时,划分出一个新的起点后,那么后面所有的数字均需要再被加上一次,不难发现,此时要加上的就是维护的数字总和

hint:由于第一个元素也可能被划分到一个新的子段中,为了避免重复计算,当遍历第一个元素时,必须将当前元素视为子段起点,加上维护的数字总和

代码

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e5 + 5;

LL a[N];

void solve() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    LL ans = 0, tmp = 0;
    for (int i = n; i >= 1; i--) {
        tmp += a[i];
        if (tmp > 0 || i == 1) {
            ans += tmp;
        }
    }
    cout << ans << endl;
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

D1. Maximum And Queries (easy version)(位运算)

题意:

给出一个长度为

n

n

n的数组

a

a

a,并进行

q

q

q次询问,每次询问为:

  • 你可以进行

    k

    k

    k操作,每次从数组

    a

    a

    a中选择一个元素,让这个元素加一

问:经过

k

k

k操作后,

a

1

&

a

2

&

.

.

.

&

a

n

a_1 & a_2 & … & a_n

a1&a2&…&an最大值是多少。

注:每次询问都是独立的,不会保留前面询问的操作结果

分析

由于每次询问都是独立的,那么修改不能在原数组上进行,需要将原数组元素复制到其他数组中再进行操作。

题目数据规定了

n

q

1

0

5

n cdot q le 10^{5}

nq105,那么可以认为最大数据就是对长度为

1

0

5

10^5

105的数组进行一次询问。

由于给出的

k

1

0

18

k le 10^{18}

k1018,那么当数组中只有一个元素时,最大可能的结果

1

0

18

+

1

0

6

2

60

10^{18} + 10^{6} approx 2^{60}

1018+106260,因此需要一个大于

60

60

60的数组记录与运算后的结果每位二进制数是多少。

对于每次询问,从二进制高位开始往低位进行遍历,每次检查当前剩余的

k

k

k是否还能将数组中所有数的当前二进制位修改为1,如果可以,进行修改,并记录在答案数组中,如果不行,继续遍历更低的二进制位

结束修改后,将数组中记录信息转化为十进制数即可。

hint本题数据较大,需要注意使用左移运算需要使用

1

l

l

1ll

1ll

1

1

1转化为long long类型再进行运算,计算花费时也要考虑如果超过操作次数就该退出检查,避免数据溢出

代码

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e5 + 5;

LL a[N], b[N], ans[105];
int n, q;

LL getCost(int x, LL k) {
    LL cost = 0;
    for (int j = 1; j <= n; j++) {
        if ((b[j] & (1ll << x)) == 0) {
            //使用位运算计算b[j]的第x二进制修改为1需要加上多少
            cost += (1ll << x) - ((1ll << (x + 1)) - 1ll & b[j]);
        }
        if (cost > k) return cost; //超过k就返回,避免数据溢出
    }
    return cost;
}


void solve(LL k) {
    memset(ans, 0, sizeof(ans));
    for (int i = 1; i <= n; i++) {
        b[i] = a[i];//复制一份数据
    }
    for (int i = 60; i >= 0; i--) {//检查第i位结果与运算的结果是否可能为1
        LL cost = getCost(i, k);
        if (cost <= k) {//花费在操作次数范围
            ans[i] = 1;//与运算结果可以为1
            k -= cost;
            for (int j = 1; j <= n; j++) {
                if ((b[j] & (1ll << i)) == 0) {
                    //通过或运算将2^i位修改为1,再通过与运算将后面数字清0
                    b[j] = ((b[j] | (1ll << i)) & (1ll << i));
                }
            }
        }
    }
    LL  res = 0;
    for (int i = 0; i <= 60; i++) {
        res |= (ans[i] << i);//这里ans[i]如果不是long long类型也会发生溢出
    }
    cout << res << endl;
}

int main() {
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    while (q--) {
        LL k;
        cin >> k;
        solve(k);
    }
    return 0;
}

E.Geo Game(构造)

题意:

注:本题交互题。

给出一个二维平面和一个起点

(

s

x

,

s

y

)

(s_x, s_y)

(sx,sy),同时,有

n

n

n个点可以被选择,每个选手每轮操作时可以选择

n

n

n个点中未被选过的一个,并在当前点与上一个点之间连一条线。

完成所有操作后,如果所有线段的长度和为偶数,那么先手获胜,否则,后手获胜。

问,该选择先手还是后手,并模拟选择棋子过程

分析

由于奇数平方也是奇数偶数平方也是偶数,且本题只在乎结果为奇数还是偶数,那么不需要对距离进行计算,根据奇偶性将距离分为

0

,

1

0, 1

0,1两种情况,即:

那么可以将所有点的

x

,

y

x,y

x,y坐标加在一起,并分为奇数和偶数两种情况存储

然后考虑该怎么选择操作。

首先,推样例可以发现一个结论:

当一个子段端点相同时,无论往里面加入多少种不同的点,最后整个子段中的线段距离总和一定为偶数。

那么想要获胜,就可以构造一个这样的子段,如果是先手,那么需要最后结果为偶数,此时如果最后选择的点奇偶性与起点相同,就是必胜的,可以想到,当与起点奇偶性相同的点数大于等于不同的点的数量,就可以每次都选择与起点奇偶性不同的点,使得最后后手只能选择一个奇偶性与起点相同的点,导致最后结果为偶数。

如果是后手,那么就是起点奇偶性相同的点数量小于不同的点的数量的情况下,每次选择与起点奇偶性相同的点,由于这部分点数量较少,被选完后,只能选择奇偶性不同的点导致结果由偶数变为奇数,而由于只有奇偶性不同的点之间连线才会改变结果的奇偶性,那么剩余的点无法再将结果变为偶数,此时后手必胜。

代码

#include <bits/stdc++.h>

typedef long long LL;
using namespace std;
const int N = 1e5 + 5;

int n, op;
set<int> id[5];

void ask() {//给出当前决策
    if (!id[op].empty()) {
        cout << *(id[op].begin()) << endl;
        id[op].erase(id[op].begin());
    } else {
        cout << (*id[op ^ 1].begin()) << endl;
        id[op ^ 1].erase(id[op ^ 1].begin());
    }
    cout.flush();
}

void get() {//接受敌手决策
    int pos;
    cin >> pos;
    if (id[0].find(pos) != id[0].end()) id[0].erase(pos);
    else id[1].erase(pos);
}


void second_solve() {
    for (int i = 1; i <= n; i++) {
        if (i % 2 == 1) get();
        else ask();
    }
}

void first_solve() {
    for (int i = 1; i <= n; i++) {
        if (i % 2 == 1) ask();
        else get();
    }
}

void solve() {
    cin >> n;
    int sx, sy;
    cin >> sx >> sy;
    op = (sx + sy) % 1;
    for (int i = 1; i <= n; i++) {
        int x, y;
        cin >> x >> y;
        id[(x + y) % 2].insert(i);
    }
    if (id[op].size() < id[op ^ 1].size()) {
        cout << "Second" << endl;
        second_solve();
    } else {
        op ^= 1;//先手选与起点奇偶不同的点
        cout << "First" << endl;
        first_solve();
    }
}

int main() {
    int Case;
    cin >> Case;
    while (Case--) {
        solve();
    }
    return 0;
}

学习交流

以下学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家加入

原文地址:https://blog.csdn.net/Code_Shark/article/details/134733691

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_24116.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注