Codeforces Round #623 (Div. 2, based on VK Cup 2019-2020 - Elimination Round, Engine)

A. Dead Pixel(思路)

思路

  • 题意:给我们一个m*n的表格,又给了我们表格中的一个点a,其坐标为(x, y),问在这个表格中选择一个不包括改点a的最大面积的矩形,输出这个最大面积

  • 分析:很明显这个点 可以将 m*n的表格分成四份,而每一份又可以分成 举行的长或宽不被点a影响的两小份,接下来就是比较讨论了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<stack>
using namespace std;

#define Max(a, b, c, d) max(max(a, b), max(c, d))
int m,n,x,y;
int Area(int x1, int y1, int x2, int y2)
{
    int s1 = abs(x1 - x2) * n;
    int s2 = abs(y1 - y2) * m;
    return max(s1, s2);
}

int main()
{
    /* freopen("A.txt","r",stdin); */
    /* freopen("Ans.txt","w",stdout); */
    int t;
    scanf("%d", &t);
    while(t --)
    {
        scanf("%d %d %d %d", &m, &n, &x, &y);
        x ++, y ++;
        int a = Area(1, 1, x, y);
        int b = Area(1, n, x, y);
        int c = Area(m, 1, x, y);
        int d = Area(m, n, x, y);
        printf("%d\n", Max(a, b, c, d));
    }


    return 0;
}

B. Homecoming(思路)

思路

  • 分析:从后往前计算路费,把所给的字符串的最后一个字符设置成 非‘A’、‘B’的字符,从倒数第二个字符开始,与前一个字符相同则 则与之前的钱数相同,否则 在之前前的基础上加上 原来的 当前讨论的字符所需的钱数

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<stack>
using namespace std;

#define Max(a, b, c, d) max(max(a, b), max(c, d))
#define ll long long 
const int Len = 1e6;
int a, b, p;
char s[Len];
ll mon[Len];

int main()
{
    /* freopen("A.txt","r",stdin); */
    /* freopen("Ans.txt","w",stdout); */
    int t;
    scanf("%d", &t);
    while(t --)
    {
        scanf("%d %d %d", &a, &b, &p);
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        mon[n] = 0;
        s[n] = 'C';
        for(int i = n - 1; i >= 1; i --)
        {
            mon[i] = mon[i + 1];
            if(s[i] != s[i + 1])
            {
                if(s[i] == 'A')
                    mon[i] += a;
                else
                    mon[i] += b;
            }
            /* printf("mon[%d] = %d\n", i, mon[i]); */
        }
        for(int i = 1; i <= n; i ++)
        {
            if(p >= mon[i])
            {
                printf("%d\n", i);
                break;
            }
        }
    }


    return 0;
}

C. Restoring Permutation(思路)

思路

  • 题意:给我们一个长度为2n的ar数组(由1~2n之间的数组成) 和一个长度为n的br数组,在br数组中的元素(由1~2n之间的数组成): b r [ i ] = m i n ( a r [ 2 ∗ i − 1 ] , a r [ 2 ∗ i ] ) br[ i ] = min(ar[2*i - 1], ar[2*i]) br[i]=min(ar[2i1],ar[2i]), 现在给我们了 br的n个元素,求出 是否一个能找出来一个满足定义br时要求序列ar,如果有输出那个字典序最小的那个,否则输出-1.

  • 分析:对于任意一对 a r [ 2 ∗ i − ] , a r [ 2 ∗ i ]    ( 1 = < i < = n ) ar[2*i - ],ar[2*i] ~~ (1=<i<= n) ar[2i]ar[2i]  (1=<i<=n),首先我们一定知道它们两个中最小的那个是br[i], 但是题目要求我们字典序最小,这时候如果有符合题意的数组ar的话,那么肯定是 a r [ 2 ∗ i − 1 ] = = b r [ i ] ar[2*i-1] == br[i] ar[2i1]==br[i](字典序最小的原因),剩下的就是根据贪心的思想,但我们从左到右 依次讨论 a r [ 2 ∗ 1 ] , a r [ 2 ∗ 2 ] , a r [ 2 ∗ 3 ] , , , a r [ 2 ∗ n ] ar[2*1],ar[2*2],ar[2*3],,,ar[2*n] ar[21],ar[22],ar[23],,,ar[2n] 这些空位的值我们应该怎么填了,首先填这些空的数字必须是 1~2*n 中没有在br中出现过的数字,但我们在遍历讨论要填某个空 a r [ 2 ∗ i ] ar[2*i] ar[2i]的值时,首先要求是 a r [ 2 ∗ i ] > a r [ 2 ∗ i − 1 ] ar[2*i] > ar[2*i-1] ar[2i]>ar[2i1],这样符合定义 b r br br 时的要求,其次是: a r [ 2 ∗ i ] ar[2*i] ar[2i]要尽可能的靠近 a r [ 2 ∗ i − 1 ] ar[2*i-1] ar[2i1],这样我们根据这个思路去循环的填写每一个空 a r [ 2 ∗ i ] ar[2*i] ar[2i],就能得到答案了

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<string>
#include<vector>
#include<queue>
#include<stack>
using namespace std;

#define Max(a, b, c, d) max(max(a, b), max(c, d))
#define ll long long 
const int Len = 1e3;
int n;
int pos[Len];
map<int, int> use;
int ans[Len];
int ar[Len];

int main()
{
    /* freopen("A.txt","r",stdin); */
    /* freopen("Ans.txt","w",stdout); */
    int t;
    scanf("%d", &t);
    while(t --)
    {
        memset(ar, 0, sizeof(ar));
        scanf("%d", &n);
        for(int i = 1; i <= n; i ++)
        {
            scanf("%d", &ar[i]);
            ans[2*i - 1] = ar[i];
            ans[2*i] = 0;
            use[ar[i]] = 1;
        }
        for(int i = 1; i <= n; i ++)
        {
            int last = 1;
            while(use[last] > 0 || last <= ar[i])
                last ++;
            if(last > 2*n) break;
            ans[2*i] = last;
            use[last] = 1;
        }
        int flag = 1;
        for(int i = 1; i <= 2*n; i ++)
            if(! ans[i])
            {
                flag = 0;
                break;
            }
        if(flag)
        {
            for(int i = 1; i <= 2*n; i ++)
                printf("%d ", ans[i]);
            printf("\n");
        }
        else
            printf("-1\n");

        use.clear();
    }


    return 0;
}

D. Recommendations(并查集+贪心)

思路

  • 题意:给我一个n个元素的序列: a r ar ar, 我可以增加这个序列中的某些元素的值,但是我们我们每将ar序列中的某个元素值+1,会耗费一定代价,又给我们一个耗费代价的序列 b r br br , 其中 b r [ i ] br[i] br[i] 把ar中的第i个元素+1的耗费代价,问我把序列ar中的元素通过 “加数操作” 边的各不相同,需要耗费的最小代价是多少??(注意:只能在原来原来的元素上添加值)

  • 分析:这一题自己做的时候很懵逼,完全没有思路,后来baidu了一下看都是用并查集写的,真是考思维啊, 现在理解的可能也不太好,首先就是 把自定义的结构体数组中的元素 首先按增加某个值代价,按从大到小排序,如果代价相同 就按ar中的数从 小到大 来拍序,首先我我想先说一个规律:对于一个ar序列如:1 3 3 4,我们想不考虑它们增加值的代价是多少,对于第一种变换方法:我们可以把 改变其中一个3->4,在改变原来的那个4改变为 4 -> 5, 这样得到的序列是 :1 3 4 5,另一种该法就是直接把 3->4,在把这个4(是由3变换过来的)变为 4->5 这样得到的序列也是 :1 3 4 5 ; 通过这样的两种变化方式,可以发现它们的 +1 的过程都经过了两次,通过这样我们的总结:对于任意一个序列 我们要想把它的各个元素变的不一样,我们所需要的最小的变化次数(即: +1 的次数)是相同的。。。这样我们无论怎么操作+1最小次数不变,那么而我们 +1是需要的费用是不同的,那么对于某个序列的任意一次+1 操作我们要尽量选择 代价小的那个数进行+1,而我们排序的规则正好符合这个要求,而并查集的真实维护的过程大概是: 对于第一出现的元素x,花费+0,然后在让x的父节点fx与fx+1,合并到一个集合中去,其中只能是 fx+1 为父节点,对于重复出现的元素,并查集已经找到了最少要变化得到的值,那直接计算这个变化的到这个值的花费是多少就行了,然后在建立节点关系,在讨论下一个元素。。。。。

代码

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

#define ll long long 
const int Len = 300000;
map<int, int> fa;
struct Node
{
    int data, cost;
    bool operator < (const Node b) const
    {
        if(cost == b.cost)
            return data < b.data; 
        return cost > b.cost;
    }
} node[Len];

int find(int x)
{
    if(fa[x] == 0) return x;
    return fa[x] = find(fa[x]);
}

void Union(int x, int y)
{
    int fx = find(x);
    int fy = find(y);
    if(fx != fy)
        fa[fx] = fy;
}

int main()
{
    /* freopen("A.txt","r",stdin); */
    fa.clear();
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)    
        scanf("%d", &node[i].data);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &node[i].cost);
    sort(node + 1, node + 1 + n);

    ll ans = 0;
    for(int i = 1; i <= n; i ++)
    {
        int fx = find(node[i].data);
        ans += node[i].cost * 1LL * (fx - node[i].data);
        Union(fx, fx + 1);
    }
    printf("%lld", ans);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值