[Codeforces Round #649 (Div. 2)]1364

6 篇文章 0 订阅



比赛首页
标准题解

A - XXXXX [思维]

目录
题目传送门

Time Limit Per TestMemory Limit Per Testinputoutput
1 seconds256 megabytesstandard inputstandard output

题意

n n n 个数的数组 a [ ] a[] a[],要使数组的总和 % k ! = 0 \% k != 0 %k!=0
可以选择删去开始的连续几个数或者删去末尾的连续几个数
问这个数组满足条件的最长长度,不存在则输出 − 1 -1 1

分析

只要找到头尾最近的一个 % k ! = 0 \% k != 0 %k!=0 的数,然后扣掉这一整段的长度

Code

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

int a[maxn];

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, x, ans = -1, sum = 0;
        scanf("%d%d", &n, &x);
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &a[i]); 
            a[i] %= x, sum += a[i];
            if(a[i])    ans = 0;
        }
        if(ans == -1) {
            puts("-1");
        }else if(sum % x) {
            printf("%d\n", n);
        }else{
            int id = 1, id2 = n;
            while(id <= n && a[id] == 0)    id += 1;
            while(id2 > id && a[id2] == 0)  id2 -= 1;
            printf("%d\n", max(n - id, id2 - 1));
        }
    }
    return 0;
}


B - Sign Flipping [思维]

目录
题目传送门

Time Limit Per TestMemory Limit Per Testinputoutput
1 seconds256 megabytesstandard inputstandard output

题意

n n n 个数的数组 a [ ] a[] a[],可以任意次 选择删去任意位置的数
最大化 ∣ a [ i + 1 ] − a [ i ] ∣ | a[i + 1] - a[i] | a[i+1]a[i] 且使得数组长度尽可能小,
则删去后的数组应为怎样的。

分析

在一段连续不递减的区间中,删除【除头尾外的元素】后,得到的结果是不变的
同理在一段连续不递减的区间中,删除【除头尾外的元素】后,得到的结果是也不变的
因此最后保留的就是山峰式的数组 【矮 高 矮 高 …】,【高 矮 高 矮…】

Code

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

int a[maxn];
vector<int> v;

int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n; v.clear();
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        if(n == 2) {
            printf("2\n%d %d\n", a[1], a[2]);
            continue;
        }
        int m = 0; v.push_back(a[1]);
        for(int i = 2; i < n; ++i) {
            if(a[i] <= v[m] && a[i] <= a[i + 1])
                v.push_back(a[i]), m += 1;
            else if(a[i] >= v[m] && a[i] >= a[i + 1])
                v.push_back(a[i]), m += 1;    
        }
        v.push_back(a[n]), m += 1;
        printf("%d\n", m + 1);
        for(int i = 0; i <= m; ++i)
            printf("%d%c", v[i], i == m ? '\n' : ' ');
    }
    return 0;
}


C - Ehab and Prefix MEXs [构造][并查集]

目录
题目传送门

Time Limit Per TestMemory Limit Per Testinputoutput
1 seconds256 megabytesstandard inputstandard output

题意
给一个数组 a [ ] a[] a[],有 n n n 个数,构造一个数组 b [ ] b[] b[]
要满足 a [ i ] = M E X ( b [ 1 ] , b [ 2 ] , . . . b [ i ] ) a[i] = MEX(b[1], b[2], ... b[i]) a[i]=MEX(b[1],b[2],...b[i])
其中 M E X MEX MEX 表示 数组中未出现过的最小的数(从0开始)
若找不到这样的数组 b b b,则输出 -1
保证数组 a [ i + 1 ] > a [ i ] & & 0 ≤ a [ i ] ≤ i a[i + 1] > a[i] \&\& 0 \leq a[i] \leq i a[i+1]>a[i]&&0a[i]i

分析
其实不管怎样都是可以构造出来的,因为保证了 a [ i + 1 ] > a [ i ] & & 0 ≤ a [ i ] ≤ i a[i + 1] > a[i] \&\& 0 \leq a[i] \leq i a[i+1]>a[i]&&0a[i]i
我的跟标准题解的做法不太一样。。
从后往前遍历
如果 a [ i ] > a [ i − 1 ] a[i] > a[i - 1] a[i]>a[i1],则令 b [ i ] = a [ i − 1 ] b[i] = a[i - 1] b[i]=a[i1] a [ i − 1 ] a[i-1] a[i1]就是前 i − 1 i-1 i1个数中最大的)
否则只有 a [ i ] = a [ i − 1 ] a[i] = a[i - 1] a[i]=a[i1] 的情况,则令 b [ i ] = a [ i ] + 1 b[i] = a[i] + 1 b[i]=a[i]+1
除此之外利用并查集查找可以选择的最小的数。
连接 a [ i ] , a [ i ] + 1 a[i], a[i] + 1 a[i],a[i]+1 是因为这个位置的 M E X = a [ i ] MEX = a[i] MEX=a[i] 的话意味着前面的数都不可以等于 a [ i ] a[i] a[i]

Code

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

int b[maxn];
int a[maxn];
int ma[maxn];
int pre[maxn * 10];

int _find(int x) {
    return x == pre[x] ? x : pre[x] = _find(pre[x]);
}

void _union(int x, int y) {
    x = _find(x), y = _find(y);
    if(x < y)   pre[x] = y;
    else        pre[y] = x;
}

int main() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < maxn * 10; ++i)   pre[i] = i;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        ma[i] = max(ma[i - 1], a[i]);
    }
    int x = 0;
    for(int i = n; i > 0; --i) {
        if(a[i] > ma[i - 1])
            b[i] = ma[i - 1];
        else
            b[i] = a[i] + 1;
        b[i] = _find(b[i]);
        _union(b[i], b[i] + 1);
        _union(a[i], a[i] + 1);
    }
    for(int i = 1; i <= n; ++i)
        printf("%d%c", b[i], i == n ? '\n' : ' ') ;
    return 0;
}



D - Ehab’s Last Corollary [dfs环长度]

目录
题目传送门

Time Limit Per TestMemory Limit Per Testinputoutput
1 seconds256 megabytesstandard inputstandard output

题意

给一个无向连通图,保证没有自环以及重边
输出可以有两种方式:
第一:输出独立点集,包含有 ⌈ k 2 ⌉ \left \lceil {\frac{k}{2}} \right \rceil 2k 个点,独立集是一组顶点,它们之间没有一条边连接。
第二:输出一个简单环,长度最大为 k k k,简单循环是一个不包含任何两次顶点的循环。

分析

如果环的长度都大于 k k k,则必定存在 ≥ ⌈ k 2 ⌉ \ge \left \lceil {\frac{k}{2}} \right \rceil 2k 个相互独立的点
只要用 d f s dfs dfs 找最小环,如果存在这样的环则直接输出
为了输出不存在的情况,用 v i s [ i ] vis[i] vis[i] 标记选择到独立集的点
如果这个点选择了,则相邻的其他所有点都要标记为不可选择
最后再输出选择的点

Code

#include <bits/stdc++.h>
#define min(a, b)   ((a) < (b) ? (a) : (b))
#define max(a, b)   ((a) < (b) ? (b) : (a))
using namespace std;
typedef long long ll;
const ll INF = 1e18;
const int inf = 2e9 + 1;
const int maxn = 2e5 + 5;

vector<int> ed[maxn << 1];
bool vis[maxn]; // 独立点集
int res[maxn]; // 每个深度的点
int dep[maxn]; // 深度
int n, m, k;

void dfs(int u, int fa, int deep) {
    res[deep] = u, dep[u] = deep;
    for(auto v : ed[u]) {
        if(v == fa) continue;
        if(dep[v] && dep[u] - dep[v] + 1 > 0 && dep[u] - dep[v] + 1 <= k) {
            printf("2\n%d\n", dep[u] - dep[v] + 1);
            for(int i = dep[v]; i <= dep[u]; ++i)
                printf("%d%c", res[i], i == dep[u] ? '\n' : ' ');
            exit(0);
        }else if(dep[v] == 0) {
            dfs(v, u, deep + 1);
        }
    }
    if(vis[u]) {
        for(auto v : ed[u]) vis[v] = false;
    }
}


int main() {
    int u, v;
    memset(dep, 0, sizeof(dep));
    memset(vis, true, sizeof(vis));
    for(int i = 1; i <= n; ++i) ed[i].clear();
    scanf("%d%d%d", &n, &m, &k);
    while(m--) {
        scanf("%d%d", &u, &v);
        ed[u].push_back(v), ed[v].push_back(u);
    }
    dfs(1, 1, 1);
    puts("1"); k = k % 2 + k / 2;
    for(int i = 1; k && i <= n; ++i) {
        if(vis[i] && k--)
            printf("%d%c", i, k ? ' ' : '\n');
    }
    return 0;
}


目录

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值