AtCoder Beginner Contest 260 A-G

AtCoder Beginner Contest 260

https://atcoder.jp/contests/abc260/tasks

A - A Unique Letter

题意

给定一个字符串,输出任意一个只出现了一次的字符

分析

直接模拟啦

Code

#include <bits/stdc++.h>

using namespace std;
int cnt[27];

int main () {
    string s;
    cin >> s;
    for (int i = 0; i < 3; i ++)
        cnt[s[i] - 'a'] ++;
    for (int i = 0; i < 26; i ++) {
        if (cnt[i] == 1) {
            cout << (char)(i + 'a');
            return 0;
        }
    }
    cout << -1;
}

B - Better Students Are Needed!

题意

现有n人,录取规则为:
数学前x名 -> 英语前y名 -> 数学加英语前z名
同分则按照编号前的先录取
按编号升序输出录取的人

分析

结构体排序,模拟即可

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 1005;
int n, x, y, z;
bool vis[N];
//int a[N], b[N]; //math,eng

struct Node {
    int id, a, b; 
}e[N];

bool cmp1 (Node x, Node y) {
    if (x.a == y.a) return x.id < y.id;
    return x.a > y.a;
}

bool cmp2 (Node x, Node y) {
    if (x.b == y.b) return x.id < y.id;
    return x.b > y.b;
}

bool cmp3 (Node x, Node y) {
    if (x.b + x.a == y.b + y.a) return x.id < y.id;
    return x.b + x.a > y.b + y.a;
}

int main () {
    cin >> n >> x >> y >> z;
    vector <int> v;
    for (int i = 1; i <= n; i ++)   cin >> e[i].a, e[i].id = i;
    for (int i = 1; i <= n; i ++)   cin >> e[i].b;

    sort (e + 1, e + n + 1, cmp1);
    for (int i = 1; i <= x; i ++) {
        v.push_back (e[i].id);
        //cout << e[i].id << endl;
        vis[e[i].id] = true;
    }

    sort (e + 1, e + n + 1, cmp2);
    for (int i = 1; i <= y; i ++) {
        if (vis[e[i].id]) {
            y ++;
            continue;
        }
        v.push_back (e[i].id);
        //cout << e[i].id << endl;
        vis[e[i].id] = true;
    }

    sort (e + 1, e + n + 1, cmp3);
    for (int i = 1; i <= z; i ++) {
        if (vis[e[i].id]) {
            z ++;
            continue;
        }
        v.push_back (e[i].id);
        //cout << e[i].id << endl;
        vis[e[i].id] = true;
    }
    sort (v.begin(), v.end());
    for (auto i : v)    cout << i << endl;
}

//数学前x名,英语前y名,数学加英语前z名
//同分则按照编号前的先录取
//输出录取的人

C - Changing Jewels

题意

现有红蓝两种宝石,每种宝石都有分不同的level
最开始给定一个level为n的红宝石,可以进行如下转化:
(两种操作)(把level位n的红宝石简称为红n)

  1. 把1个红n变成: 1个红n-1 + X个蓝n
  2. 把1个蓝n变成: 1个红n-1 + Y个蓝n-1
    问最多能有多少个蓝1

分析

模拟递推式即可,注意要先处理2.再处理1.,因为蓝n可以在1.中产生,所以要先求出他的值
\(b_i\) 表示为 1 个level为 i 的蓝宝石能够产生多少个level为1的蓝宝石,
\(r_i\) 表示为 1 个level为 i 的红宝石能够产生多少个level为1的蓝宝石。

Code

#include <bits/stdc++.h>
#define int long long

using namespace std;
int r[15], b[15];

signed main () {
    int n, x, y;
    cin >> n >> x >> y;

    if (n < 2) {
        cout << 0;
        return 0;
    }

    r[1] = 0, b[1] = 1;
    for (int i = 2; i <= n; i ++) {
        b[i] = r[i-1] + b[i-1] * y; //先更新他,因为后面要按用到b[i]
        r[i] = r[i-1] + b[i] * x;
    }
    cout << r[n] << endl;
    
}

//两种操作
//1. 把1个红n变成: 1个红n-1 + X个蓝n
//2. 把1个蓝n变成: 1个红n-1 + Y个蓝n-1
//问最多能有多少个蓝1

//好小的范围
//蓝的里面还有红的
//递推,预处理
//模拟递推式即可

D - Draw Your Cards

题意

现有n张牌叠在一起,每次从最上面取出一张牌(上面的值为x),面朝上放在桌上,放置规则为:

  1. 找到已经在桌上的牌中第一个>=x的,叠在上面(更新该处的值)
  2. 若每日找到符合要求的,则新开一堆(单张牌放着)

如果某一堆摊开的牌个数达到了k,就可以把这一堆拿走

按照牌序号1-n的顺序输出该牌是什么时候被拿走的,没被拿走的牌则输出-1

分析

模拟。set 维护每一堆牌的数量以及顶部的那张牌的值
二分查找值来实现更新

有一个不太好处理的点,就是在拿走k张牌的时候,如何找到路径呢?
那么可以用一个数组来迭代保存路径(有点像链表一样,往回找)

然后因为取牌是有顺序的,所以可以在线处理

注意:要用set自带的lower_bound,效率会高很多很多

效率差距:

Code

#include <bits/stdc++.h>

using namespace std;
typedef pair<int, int> pii;
const int N = 2e5 + 5;
int cnt[N], nxt[N]; //数量 路径

int main () {
    int n, k;
    cin >> n >> k;
    vector <int> ans (n+1, -1);

    set <int> table; //维护桌上的最大值
    for (int i = 1; i <= n; i ++) {
        int x;  cin >> x;
        if (k == 1) {
            ans[x] = i;
            continue;
        }
        auto pos = table.lower_bound (x);
        //cout << "pos=" << *pos << endl;
        //开一叠新的
        if (pos == table.end()) {
            table.insert (x);
            cnt[x] ++;
        }
        //叠上+更新值
        else {
            nxt[x] = *pos;
            cnt[x] = cnt[*pos] + 1;
            table.erase (pos), table.insert (x);
        }

        //能被拿走
        if (cnt[x] == k) {
            table.erase(x);
            int tk = k, tx = x;
            while (tk --) {
                ans[tx] = i;
                tx = nxt[tx];
            }
        }
    }

    for (int i = 1; i <= n; i ++)   cout << ans[i] << endl;
}

//很像蜘蛛纸牌
// 把最上面的牌拿出来,上面的值为x
// 找到已经翻开的牌中第一个>=x的,叠在上面(更新该处的值)
// 如果某一堆摊开的牌个数达到了k

// 1-n输出该牌是什么时候被吃掉的,没被吃掉则输出-1
// 模拟+二分查找

E - At Least One

题意

给定 n 个数对 \((A_i,B_i)\),对于一段连续区间 \([l,r]\), 若对于所有数对 \((A_i,B_i)\) 满足
\(l\leq A_i\leq r\)\(l\leq B_i\leq r\),则该区间是 好区间
问长度为1,2,3,...,m 的好区间分别有多少个

分析

易得出一条性质:如果 \([l,r]\)是一个好区间,那么所有包含 \([l,r]\) 的区间都是好区间,所以只需求出一个最小的好区间 \([l,r]\),然后就能根据差分等算出区间个数了

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 2e5 + 5;
int cnt[N], ans[N];

int main () {
    int n, m;
    cin >> n >> m;
    vector <int> pos[m+1];

    for (int i = 1; i <= n; i ++) {
        int a, b;
        cin >> a >> b;
        pos[a].push_back (i), pos[b].push_back (i);
    }

    int res = n;
    for (int l = 1, r = 1; l <= m; l ++) {
        //滑动窗口
        //固定l,找到满足条件的最小右边界r
        while (r <= m && res) {
            for (auto i : pos[r]) {
                if (cnt[i] == 0)    res --;
                cnt[i] ++;
            }
            r ++;
        }

        if (res)    break; //再往后的都不满足要求了
        ans[r-l] ++, ans[m+1-l+1] --; //[l,r]满足条件,则长度在[r-l+1, m-l+1]的都满足

        //l右移,消除原本贡献
        for (auto i : pos[l]) {
            cnt[i] --;
            if (cnt[i] == 0)    res ++;
        }
    }

    for (int i = 1; i <= m; i ++) {
        ans[i] += ans[i-1];
        cout << ans[i] << ' ';
    }
}

//差分+双指针

F - Find 4-cycle

题意

给定一个无向图,两个独立集(集合内部的点没有被边连接S,T ,有 [m 条无向边,请找到一个四元环(有四个点的环),若没有,输出 -1

分析

四元环必有两个点来自S,两个点来自T(因为独立集的性质保证了集合内部没有边相连),所以一条边的两个端点一定来自不同集合,不难推出,四元环必然是"一点在S,一点在T,一点在S,一点在T"这样交替出现的形式

观察发现 T 小,所以可以枚举S中的点a ,并枚举与其相连的两个(在T中的)的点b,d, 那么当且仅当 S中存在一点c,与b,d相连时,四元环存在

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 3e3 + 5, M = 5e5 + 5;
vector <int> T[M]; //T集
int f[N][N];

int main () {
    int n, m, k;
    cin >> n >> m >> k;
    while (k --) {
        int x, y;
        cin >> x >> y;
        T[x].push_back (y-n);
    }

    for (int i = 1; i <= n; i ++) {
        for (auto x : T[i])
            for (auto y : T[i]) {
                if (x == y) continue;
                if (!f[x][y])    f[x][y] = i;
                else {
                    cout << i << ' ' << x+n << ' ' << f[x][y] << ' ' << y+n << endl;
                    return 0;
                }
            }
    }
    cout << "-1\n";
}

G - Scalene Triangle Area

题意

给定大小为 \(N*N\)\(OX\) 矩阵,若某点 \((x,y)\) 的为\(O\),其覆盖范围为:满足以下条件的所有位置\((i,j)\)

  1. \(s <= i ,\, t <= j\)
  2. \((i - s)\) + \(\frac{j - t}2\) < M

q次询问,对于每次询问\((x,y)\),要求给出对应位置被覆盖了多少次

分析

一开始不是很懂,看这个理解的

一些类似差分的思想,假如只有最左上角是O,那么覆盖状况如表1所示:
(由覆盖的性质易得,单点所形成的被覆盖的图形会呈现 三角形 的样子)


(图源自上面的链接)

表2是对于每一行进行求和,+号表示求和开始的位置,-号表示求和结束的位置;
同理,表3是对竖行进行求和(即把每行的累加过来)
表4是感觉看不出来啥用(标记起点终点?)

然后按行列递推求即可:

行:(直接加到上一个)add[i][j] += add[i][j - 1], del[i][j] += del[i][j - 1]
列:(呈阶梯状)add[i][j] += add[i - 1][j], del[i][j] += del[i - 1][j + 2]

Code

#include <bits/stdc++.h>

using namespace std;
const int N = 2005;
int add[N][5*N], del[N][5*N];
string s[N];

int main() {
    int n, m;
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        cin >> s[i];
        for(int j = 1; j <= n; j++)
            if(s[i][j-1] == 'O') {
                ++ add[i][j], -- del[i][j + 2 * m]; //横
                -- add[min(i+m, n+1)][j], ++ del[min(i+m, n+1)][j]; //纵
            }
    }

    //纵
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n + 2 * m; j++) 
            add[i][j] += add[i-1][j], del[i][j] += del[i-1][j+2];

    //横
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++) 
            add[i][j] += add[i][j-1], del[i][j] += del[i][j-1];
    
    int q;
    cin >> q;
    while(q --) {
        int x, y;
        cin >> x >> y;
        cout << add[x][y] + del[x][y] << endl;
    }
}


// 给定大小为N*N的OX矩阵,若矩阵的(s,t)处为O,其覆盖范围为:满足以下条件的所有位置(i,j)
// s <= i && t <= j
// (i - s) + (j - t) / 2 < M
// 再给出Q次询问,对于每次询问(x,y),要求给出对应位置被覆盖了多少次。

//纵向横向累加,维护+表和-表
posted @ 2022-07-21 11:42  Sakana~  阅读(298)  评论(3编辑  收藏  举报