Codeforces Round #799 (Div. 4) (A-H)全题解

A:

问你比第一个数大的数有几个,直接枚举

int main()
{
    IOS;
    int n;
    cin >> n;
    while(n -- )
    {
        int cnt = 0;
        int x;
        cin >> x;
        for (int i = 0; i < 3; i ++ )
        {
            int a;
            cin >> a;
            if (a > x)  cnt ++;
        }        
        cout << cnt << endl;
    }
    
    return 0;
}

B:

每次可以删除任意位置的两个数,问你剩下的不重复的数最多有多少个。

首先:答案<=不重复数的个数。其次看奇偶性,如果不重复个数的奇偶性和n一样,那么答案就是不重复个数,否则-1

int main()
{
    IOS;
    int t;
    cin >> t;
    while (t -- )
    {
        int n;
        cin >> n;
        set<int> s;
        for (int i = 0; i < n;i ++ )
        {
            int x;
            cin >> x;
            s.insert(x);
        }
        if(n % 2 == 0 && s.size() % 2 == 0 || n % 2 && s.size() % 2)  cout << s.size() << endl;
        else    cout << s.size() - 1 << endl;
    }
    
    return 0;
}

C: 

问你哪一个位置是皇后,皇后必须满足四个角都是'#',直接判断即可

const int mod = 1e9+7;
char g[10][10];
 
 
int main()
{
    IOS;
    int t;
    cin >> t;
    while (t -- )
    {
        for (int i = 0; i < 8; i ++ ) cin >> g[i];
        
        for (int i = 1; i < 7; i ++ )
        {
            for (int j = 1; j < 7; j ++ )
            {
                if(g[i][j] == '#' && g[i - 1][j - 1] == '#' && g[i - 1][j + 1] == '#' && g[i + 1][j - 1] == '#' && g[i + 1][j + 1] == '#')
                {
                    cout << i + 1 << ' ' << j + 1 << endl;
                    break;
                }
            }
        }
    }
    
    return 0;
}

D: 

给你一个初始时间和间隔t,问你在一天的时间之内,每次隔间t时间看一下当前的时间,如果当前时间的时和分符合回文数的话,那么cnt++,问最后的cnt。

首先把时间表示成一个整数,用整数去加间隔时间,再分离出当前时间的时和分,判断一下即可。

int main()
{
    int t;
    cin >> t;
    while (t -- ){
        int a, b, d;
        int cnt = 0;
        scanf("%d:%d", &a, &b);
        cin >> d;
        int be = a * 60 + b;
        int x = be / 60;
        int y = be % 60;
        x = (x % 10 * 10) + x / 10;
        if(x == y)      cnt ++;
        int k = be;
        while(1){
            be += d;
            be %= 1440;
            if(be == k) break;
            int x = be / 60;
            int y = be % 60;
            x = (x % 10 * 10) + x / 10;
            if(x == y)      cnt ++;
        }
        cout << cnt << endl;
    }
    
    return 0;
}

 E:

每次操作可以删除队头或者队尾,问你至少要删除多少次,使得剩下的元素之和为s。

就是问你哪个区间和为s且长度最大,那么删除次数就等于总长度减去区间长度。直接前缀和维护即可
要算某一个区间的和为k,且区间长度是最大的,则我们需要首先预处理出来前缀和
因为某一个区间的和等于a[r] - a[l - 1],对于每一个固定的a[i],我们需要找出它的a[l - 1]
出现的位置,那么区间长度就是对应下标减一减


const int N = 2e5 + 10;
int a[N];//前缀和数组
int b[N];//记录下标的数组
 
int main()
{
    IOS;
    int _;
    cin >> _;
    while(_ --){
        int n, k;
        cin >> n >> k;
        int ans = n;
        for (int i = 1; i <= n; i ++ )cin >> a[i];
        for (int i = 1; i <= n; i ++ )a[i] += a[i - 1];
        if(a[n] < k){
            cout << -1 << endl;
            continue;
        }
        for (int i = 1; i <= n; i ++ )      b[i] = -1;
        b[0] = 0;
        for (int i = 1; i <= n; i ++ ){
            if(a[i] - k >= 0 && b[a[i] - k] != -1)     ans = min(ans, n -(i - b[a[i] - k]));
            if(b[a[i]] == -1)       b[a[i]] = i;
        }
        cout << ans << endl;
    }
    
    return 0;
}

F:

问你是否有三个不同的数,满足三者和的个位是3.

那么我们可以读入的时候就只读入个位,那么枚举1个a[i],再枚举0-9中的第二位,再看一下第三位是否存在。

const int N = 300010;
int a[N];
 
int main()
{
    IOS;
    int _;
    cin >> _;
    while (_ -- ){
        int n;
        cin >> n;
        int q[11] = {0};
        for (int i = 0; i < n; i ++ )
        {
            int x;
            cin >> x;
            a[i] = x % 10;
            q[x % 10] ++;
        }
        
        int ok = 0;
        for (int i = 0; i < n; i ++ ){
            q[a[i]] -- ;//用到了这一位,先减去
            int t = (3 - a[i] + 10) % 10;//剩下两位的和
            for (int x = 0; x < 10; x ++ ){//枚举剩下两位中的一位
                int y = (t - x + 10) % 10;//这是最后一位
                
                if(x == y && q[x] >= 2) ok = 1;//如果这两位相等的话,那么必须出现过至少2次
                else if(x != y && q[x] && q[y])    ok = 1;//如果不相等并且都出现过
            }
            q[a[i]] ++;//恢复一下
        }
        if(ok)  cout << "YES" << endl;
        else    cout << "NO" << endl;
    }
    
    return 0;
}

G: 

问你是否存在多少个这样一个长度为k+1的序列,满足a[i] < 2 * a[i + 1];

我们可以处理出一个长度为n-1的序列b,b[i]=1则表示a[i] < 2 * a[i + 1],那么b显然是一个01序列。我们只需要看这个序列中有多少个长度大于等于k的连续子序列即可。显然我们可以从后往前dp一下,(其实也不用dp,就是一个很简单的事情)。然后循环一遍dp数组,看一下有多少位置的连续子序列长度大于等于k即可

#include<bits/stdc++.h>
using namespace std;

#define x first
#define y second
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;

ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 1e9+7;
/*
根据题目我们可以直接创造出n-1长度的01序列,表示当前位置与下一个位置是否满足关系,然后只需要看当前位置连续1的个数是否>=k
*/
const int N = 200010;
//int a[N], b[N];//b数组记录bool判断
//int dp[N];//反向做一遍dp,看当前位置的连续1的个数

int main()
{
    IOS;
    int _;
    cin >> _;
    while(_ -- ){
        int n, k;
        cin >> n >> k;
        int a[n] = {0}, b[n] = {0}, dp[n] = {0};
        for (int i = 0; i < n; i ++ )   cin >> a[i];
        for (int i = 0; i < n - 1; i ++ )   if(a[i] < 2 * a[i + 1]) b[i] = 1;
        
        for (int i = n - 2; i >= 0; i -- ){
            if(b[i] == 1)   dp[i] = 1;
            else    dp[i] = 0;
            if(dp[i + 1] && b[i])   dp[i] += dp[i + 1];
        }
        int cnt = 0;
        for (int i = 0; i < n - 1; i ++ )   if(dp[i] >= k)  cnt ++;

        cout << cnt << endl;
    }
    
    return 0;
}

H: 

一个赌博游戏,我们呢已经知道了接下来n次筛子能抛到几。现在有这样一个问题,由于我们只能在区间[l,, r]中参与游戏,我们认为我们没参与之前有1块钱,每次赢了可以翻一倍,输了/2,问我们在哪个区间[l,r],赌筛子抛到几(x),我们可以赚的钱最多(注意在这个区间,我们每次都是赌筛子抛到x);

思路:假设我们确定了我们赌博的点数k,那么如何能赢得最多呢,显然是在区间[l,r]中k出现的次数-其他出现的次数最大!也就是众数-其他数出现的次数最大!那么如何做呢?

我们可以事先维护出每一数字出现的位置,用map<int, vector<int>>来维护。

显示我们可以知道一个结论,如果想赢得多,这个区间的左右端点必须都赚钱,不然不就白白输了嘛。所以我们可以枚举每一个出现的数在他的位置区间内出现的次数 - 其他数的次数。

我们遍历map,当前赌的是x,假设我们固定了右端点r=a[i], l = a[j],则这个区间内一共有i - j + 1个x,一共有a[i] - a[j] + 1个数,所以次数就是 (i - j + 1) - (a[i] - a[j] + 1 - (i - j + 1))整理一下可以得到是2i - ai + 1 + aj - 2j。那么当我们遍历a的时候,显然2i - ai是固定的,我们只需要看当前这个i前面的aj - 2j最大是多少即可,动态维护一下就好了。下面见代码

#include<bits/stdc++.h>
using namespace std;

#define fi first
#define y second
#define rep(i,a,n) for (int i = a; i < n; i ++ )
#define repn(i,a,n) for (int i = a; i <= n; i ++ )
#define pb push_back
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef long long ll;
typedef pair<int,int> PII;

ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
const int mod = 1e9+7;
/*
首先我们可以得到一个性质,这个区间的左右端点必然是赢钱的,不然就会莫名其妙亏了,然后呢这个问题的本质就是问你
区间内众数-非众数的个数最大是多少。
那么我们可以记录每一个元素出现的位置,用map<int, vector<int>> 来存储,vector是a,位置下标数组
然后我们去遍历这个map,固定了一个右端点ai,我们则可以发现,这个答案就是(i -j + 1)- (a[i] - a[j] + 1 - (i - j + 1)
== 2i - ai + 1 + aj - 2j,后面的j是不固定的
*/
const int INF = 0x3f;


int main()
{
    IOS;
    int _;
    cin >> _;
    while (_ -- ){
        int n;
        cin >> n;
        map<int, vector<int>> v;
        for (int i = 1; i <= n; i ++ ){
            int x;
            cin >> x;
            v[x].pb(i);
        }
        int ans = 0, l, r;//ans是赌博猜的点数,l,r是区间
        int mx = 0;//赚钱次数
        for(auto [x, a] : v){
            int t = -INF, pos;
            for (int i = 0; i < a.size(); i ++ ){
                if(a[i] - 2 * i > t){//如果说这个值大了就更新一下
                    t = a[i] - 2 * i;
                    pos = a[i];
                }
                int now = 2 * i - a[i] + t + 1;//现在可以赚钱的次数
                if(now > mx){
                    mx = now, ans = x, l = pos, r = a[i];
                }
            }
        }
        cout << ans << ' ' << l << ' ' << r << endl;
    }
    
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值