Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)

A Game
题意:给定一序列,1为陆地,0为水,在陆地可以免费移动,不能走到水中,序列左右两端都是陆地。有水的地方只能通过花费金币飞行通过,一格一个金币(不管是否是陆地还是水)且只能飞行一次,问最少花费为多少可以从左端到达右端。
题解:一开始没注意只能飞行一次,101101这种只能从开头1一次飞到右端1,总花费为5,其实只需要遍历一遍,记录最开始水的坐标x和最后的坐标y,答案就是y-x+2(如果有水的话)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;


int main()
{
    int t; cin >> t;

    while(t -- )
    {
        int n; cin >> n;
        int a[110];
        memset(a, 0x3f, sizeof a);

        int ans = 0, idx = 1;
        for(int i = 1; i <= n; i ++ ) cin >> a[i];

        int l = 0;
        int b = 0, e = 0;
        for(int i = 1; i <= n; i ++ )
        {
            if(a[i] == 0) {
                if(a[i - 1] == 1)
                {
                    if(l == 0) b = i;
                    l ++;
                }
                if(a[i + 1] == 1)
                {
                    e = i;
                }
            }
        }
        if(b == 0) cout << 0 << endl;
        else cout << e - b + 2 << endl;
    }

    return 0;
}

B Game of Ball Passing
题意:给一个序列,第i个序列表示第i个球员踢球的次数,接球不算,问一共需要踢多少个球
题解:极端情况一个球将最大值队员之外的其余队员都给踢满,看看最大值队员还剩多少次,一次踢一个球给任意一个队员。以1 5 2为例,极端情况为2-》1-》2-》3-》2-》3-》2-》3,这种情况下2号球员还差1次,而其余球员踢的次数已经满足了,所以只能另换一只球,由2发球踢给其他人。
球的最小个数,找序列最大值,与其他剩余次数求差值再减个1(因为最大值的那个球员最后可以踢一次给任意一个球员)

#include <iostream>

using namespace std;

int a[100010];

int main()
{
    int t; scanf("%d", &t);

    while(t -- )
    {
        int n; scanf("%d", &n);
        int mx = 0;
        long long sum = 0;
        bool st = true;
        for(int i = 1; i <= n; i ++ ) {
            scanf("%d", &a[i]);
            if(a[i]) st = false;
            mx = max(a[i], mx);
            sum += a[i];
        }
        sum -= mx;

        if(!st)//次数不全为0的情况下,至少也得踢一次球
            printf("%d\n", 1 + max((long long)0, mx - sum - 1));
        else printf("0\n");
    }

    return 0;
}

C Weird Sum
本来用vector<pair<int,int> >直接记录每个颜色所在的节点,直接进行三重循环判断,直接一手TLE。时间复杂度为O(c*n^2),c最差为1e5,二维矩阵内元素最多为1e10,每个元素可以有1e5个,即n为1e5。。。
由于记录二维坐标不能进行排序,dis=|xi - xj| + |yi - yj|发现x和y坐标可以分开进行求解,将颜色相同的分别存储,从小到大排序,若这个颜色的坐标只有一个,则不用计算,否则进行计算,以x坐标为例,按照暴力的想法,xi会被加i - 1次,也会被减v.size() - i次,注意xi的i从0开始,需要微调一下

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

vector<int> x[N], y[N];

int main()
{
    int n, m; cin >> n >> m;

    int mx = 0;
    for(int i = 1; i <= n; i ++ )
        for(int j = 1; j <= m; j ++ ){
            int nu;
            scanf("%d", &nu); mx = max(nu, mx);
            x[nu].push_back(i);
            y[nu].push_back(j);
        }

    long long ans = 0;
    for(int i = 1; i <= mx; i ++ ){
        if(x[i].size() == 1 || y[i].size() == 1) continue;

        sort(x[i].begin(), x[i].end());
        sort(y[i].begin(), y[i].end());

        for(int j = 0; j < x[i].size(); j ++ ){
            int t = x[i][j];
            ans += (long long)j * t;
            ans -= (long long)(x[i].size() - j - 1) * t;
        }

        for(int j = 0; j < y[i].size(); j ++ )
        {
            int t = y[i][j];
            ans += (long long) j * t;
            ans -= (long long)(y[i].size() - j - 1) * t;
        }
    }

    cout << ans << endl;

    return 0;
}

D Integral Array
题意:给定一序列a,明确ai小于c,若序列a中任意两值x,y(x > y),⌊x/y⌋也在序列a中,则输出yes,否则输出no
题解:由于给定x,y,就可以判断哪些数可从⌊x/y⌋和y中推出,即确定一个范围,使得在这个范围内的所有数除y都等于⌊x/y⌋,例如,给定x=38,y=7,则范围35~41内的所有数除y都为⌊x/y⌋。
即: ⌊ x / y ⌋ ∗ y < = x ⌊x/y⌋ * y <= x x/yy<=x ⌊ x / y ⌋ ∗ y + y > x ⌊x/y⌋ * y + y > x x/yy+y>x
r = ⌊ x / y ⌋ r = ⌊x/y⌋ r=x/y若r不在序列a中,y在序列a中,则x一定也不在序列a中,可通过前缀和判断该区间是否有值。

注意这种为调和级数复杂度问题,时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)

for (int i = 1; i <= lim; i ++)//lim为上届
  {        
    for (int j = i; j * i <= lim; j ++)
  }

除此之外,这个题目还卡住我的地方在于memset,我经常写memset(a, 0, sizeof a);将内容全部更新,时间复杂度为O(n),当t变得特别大时,memset就会超时(很离谱)所以这里写成memset(a,0,sizeof(int)*(c+5));只对需要的内容进行刷新

#include <bits/stdc++.h>

using namespace std;

const int N = 1e6 + 10;

int a[N];
int sum[N];
int st[N];

int main()
{
    int t; scanf("%d", &t);

    while(t -- )
    {
        int n, c; scanf("%d%d", &n, &c);

        memset(st, 0, sizeof(bool) * (c+ 5));//注意memset的时间复杂度,如果全部更新则会超时
        //!!!!!!!!!
        memset(sum, 0, sizeof(int) * (c + 5));
        memset(a, 0, sizeof a);

        for(int i = 1; i <= n; i ++ )
        {
            cin >> a[i];
            st[a[i]] = 1;
        }

        for(int i = 1; i <= c; i ++ )
            sum[i] = sum[i - 1] + st[i];

        bool s = true;

        for(int i = 1; i <= c; i ++ )
        {
            if(!st[i]) continue;//y = i,选择y在序列中
		
            for(int j = 1; j * i <= c; j ++ )
            {
                if(st[j]) continue;//r = j,选择r在不在序列中

                int x = sum[min(c, (j + 1) * i - 1)] - sum[i * j - 1];
//需要判断(j + 1) * i - 1是否超过c,若超过则取c值
                if(x > 0) {s = false; break;}
            }
            if(!s) break;
        }

        if(!s) puts("No");
        else puts("Yes");
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值