codeforces #550 div3 A-G

比赛的时候只做出了A-D(太菜 ),然后到现在才补掉了这套题(太懒 ),以下是各题简单叙述,前四题代码就不贴了。

A

Diverse Strings

题目大意

对于给定的字符串,如果是由一段连续的不重复的字母组成,则输出“yes”。

解题思路

直接排序后,遍历检查即可。

B

Parity Alternated Deletions

题目大意

给出n个数,每次可以任意删除一个与上次不同奇偶性的数字(第一次可以随便选),问最后剩余的数字的和最小是多少。

解题思路

统计出序列中奇数和偶数的个数a,b,然后分情况:

  1. abs(a-b)<=1 输出0即可。
  2. a-b>1,说明会剩下a-b-1个奇数,找出最小的那a-b-1个奇数即可。
  3. b-a>1,说明会剩下b-a-1个偶数,找出最小的那b-a-1个偶数即可 。
    注意开longlong

C

Two Shuffled Sequences

题目大意

给出n个数,问能否拆成两个序列,一个严格递增,一个严格递增(可以任意重排)。任意排列这点就大大简化问题了。

解题思路

考虑到可以任意排序,以及严格这两个关键点,读入的时候用一个桶记录所有数字出现的次数。

  1. 某个数字出现次数大于3说明肯定无解(必然有一个序列会出现两个数字相等的情况)。
  2. 某个数字出现次数等于2,分别放在两个序列即可。
  3. 出现一次的数,随便放即可。(长度为0或1的序列可被任意认为递增或者递减)。

D

Equalize Them All

题目大意

给出n个数字的序列,每次可以选择相邻的两个元素,使其中一个元素加上或者减去这两个元素差的绝对值,询问最少需要几次操作,使得所有数字相等,并输出每次操作。ai=ai±|ai−aj|。

解题思路

显然的 贪心题,先找出次数出现最多的那个元素的位置(用桶数组来维护,同C题),然后从那个位置分别向两端扩展,每次让边上的一个元素与它相等即可。

E

Median String
看F题过的人比较多,就没看E这道字符串题,不过感觉挺简单的 。。。

题目大意

给出两个字符串,让你给出排在两个字符串字典序中间的字符串。

解题思路

把字符串当作一个26进制数,由于长度达到2e5,所以需要用大数模拟。从后往前遍历,对于第i位,如果ai+bi是偶数,直接除2即可,如果是奇数,就要向后一位进13(26/2),然后处理一下后一位的进位(/26,%26)。(想想十进制下的奇数除以2)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 2e5 + 5;
char s[N], t[N];
int ans[N];
int main()
{
    int k;
    scanf("%d%s%s",&k,s,t);
    for(int i=k-1;i>=0;i--)
    {
        int a = s[i] - 97, b = t[i] - 97;
        ans[i] = a + b;
        if(ans[i]&1)
        {
            ans[i] = (ans[i]-1) >> 1;
            ans[i+1] += 13;
            ans[i] += ans[i+1]/26;
            ans[i+1] %= 26;
        }
        else ans[i] >>= 1;
    }
    for(int i=0;i<k;i++) printf("%c",ans[i]+97);
    puts("");
    return 0;
}

F

Graph Without Long Directed Paths

题目大意

给出n个点m条边的无环图,问你能否构造出一个有向图(将每条给出的双向边改成有向边),使得任意两点间距离不能大于等于2。

解题思路

二分图判定的染色法,对于一个顶点,不可能同时有入度和出度,要么只有入边,要么只有出边,所以只需要dfs对每一个顶点染色(1代表只有入度,-1代表只有出度)。
应该用vector存图的,然而我强行用链式前向星,没法记录编号,所以代码很丑陋。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 2e5 + 5;
int n, m, cnt, head[N], color[N], ans[N];
bool flag = true;
struct node
{
    int to, next, dir, id;
}e[N<<1];
void add(int u,int v,int w,int id)
{
    e[++cnt].next = head[u];
    e[cnt].to = v;
    e[cnt].dir = w;
    e[cnt].id = id;
    head[u] = cnt;
}
void dfs(int u,int col)
{
    if(!flag) return;
    color[u] = col; 
    for(int i=head[u];i;i=e[i].next)
    {
        int v = e[i].to;
        if(color[v]==col)
        {
            flag = false;  //全局变量判断无解的情况
            return;
        }
        int d;  //奇怪的分类讨论
        if(!e[i].dir&&col==1) d = 1;
        else if(!e[i].dir&&col==-1) d = 0;
        else if(e[i].dir&&col==1) d = 0;
        else d = 1;
        ans[e[i].id] = d;  //这句话不能放在if(!color[v])里面,因为这个一直wa
        if(!color[v]) dfs(v,-col);
        if(!flag) return;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    int u, v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v,0,i);
        add(v,u,1,i);
    }
    dfs(1,1);
    if(!flag) puts("NO");
    else
    {
        puts("YES");
        for(int i=1;i<=m;i++) printf("%d",ans[i]);
        puts("");
    } 
    return 0;
}

G

Two Merged Sequences
C的强化版,不能重排了。

题目大意

对于给定的n个数组成的序列,问能否按照原序拆成一个严格递增,一个严格递减的序列。cf给的标签是贪心和dp。

解题思路

如果是dp的话,用dp[i][0]记录第i个数字放在升序序列中时,此时降序序列的最小元素的大小,dp[i][1]记录第i个数字放在降序序列中时,此时升序序列的最大元素的大小,然后转移即可。(有点绕)当i放在升序序列时,a[i]就是升序序列的最大元素,所以只需要记录降序序列即可,do[i][1]同理。
考虑贪心,每次也是维护升序序列的最大值up和降序序列的最小值down,然后讨论。

  1. a[i]>up&&a[i]>down 只能放在升序序列,那就放呗,然后更新up=a[i]
  2. a[i]<down&&a[i]<up 只能放在降序序列,那也放呗,然后down=a[i]
  3. a[i]>up&&a[i]<down 两个地方都能放,那考虑后一个元素(给后面留条路),如果a[i+1]大于a[i],就放在升序,否则放在降序。(核心)
  4. 哪都放不了,那就输出NO。
    下面是贪心代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 2e5 + 5;
int n, a[N];
bool ans[N], flag = true;
int main()
{
    int pos = 0, minn = 0x3f3f3f3f;
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int up = -1, down = N;
    for(int i=1;i<=n;i++)
    {
        if(a[i]<down&&a[i]>up)
        {
            if(a[i+1]>a[i]||i==n)
            {
                up = a[i];
                ans[i] = 0;
            }
            else
            {
                down = a[i];
                ans[i] = 1;
            }
        }
        else if(a[i]<down)
        {
            down = a[i];
            ans[i] = 1;
        }
        else if(a[i]>up)
        {
            up = a[i];
            ans[i] = 0;
        }
        else
        {
            flag = false;
            break;
        }
        
    }
    if(!flag) puts("NO");
    else 
    {
        puts("YES");
        for(int i=1;i<=n;i++) printf("%d ",ans[i]);
        puts("");
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值