首页 > 其他 > 详细

codeforces 1291 题解

时间:2020-02-04 12:28:56      阅读:58      评论:0      收藏:0      [点我收藏+]

codeforces 1291 题解

A. Even But Not Even

要求这个数本身不能被 \(2\) 整除,加和可以被 \(2\) 整除,那我们分类讨论

  • 若该数本身已经不能被 \(2\) 整除,但是加和可以,那我们什么都不用做即可;若加和不可以,我们从后往前删一个奇数即可。
  • 若该数本身就不能被 \(2\) 整除,我们从后往前删奇数,这样就能转化成上一种情况。

删到最后删不动了或者删空了就是不行,输出 \(-1\)

时间复杂度 \(O(n)\),代码写的有点乱。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 50003
#define X first
#define Y second
 
int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while(t--)
    {
        string s;int n;
        cin >> n >> s;
        int a = 0, b = 0;
        int index = -1;
        a = s[s.size()-1]-'0';
        _for(i,0,s.size())
            b += s[i]-'0';
        
        if(!(a&0x1))
        {
            while(!s.empty() && !((s[s.size()-1]-'0')&0x1))
                s.pop_back();
        }
        
        if(b&0x1)
        {
            for(int i = s.size()-2; i>=0; i --)
            {
                if((s[i]-'0')&0x1)
                {
                    index = i;
                    break;
                }
                else
                    continue;
            }
        }
        else
            index = 100000;
        if(s.empty() || index==-1)
            printf("-1\n");
        else
        {
            _for(i,0,s.size())
                if(i!=index)
                    printf("%c",s[i]);
            printf("\n");
        }
    } 
    return 0;
}

B. Array Sharpening

他问你能不能构造出来,我们先假设能构造出来,那数组就分成两部分,我们将前一部分称为 上升部分,后一部分称为 下降部分。

我们希望什么呢?因为改变数字大小不计代价,所以上升部分越靠前的数越小越好,最小能是多少?答案就是下标为 \(i(1≤i≤n)\) 的地方最小为 \(i-1\),好给上升部分后面的数“腾位置”。

真就有一个元素达不到这个标准怎么办( \(a[i] < i-1\) )?那我们假设已经进入了 下降部分。首先有一点,下降部分都不能比 顶峰(上升部分的最后一个元素或者看作下降部分的第一个元素)大,或者一样大。其次我们想让他越大越好,这是给下降部分后面的数 "腾位置"。如果还有一个元素不能满足标准,他不够大,不能给后面的元素预留最小的足够空间(\(a[i]<n-i-1\)),那我们就知道,这数组变不成我们想要的数组了。

时间复杂度 \(O(n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 350003
#define X first
#define Y second

int a[maxn];

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    while(t--)
    {
        int n;
        cin >> n;
        _for(i,0,n)
            cin >> a[i];
        
        int can = 1;
        int st = -1;
        _for(i,0,n)
            if(a[i]<i)
            {
                st = i;
                break;
            }
        
        if(st==-1)
            goto end;
        
        
        
        _for(i,st,n)
        {
            a[i] = min(a[i],a[st-1]-1);
            if(a[i]<n-i-1)
            {
                can = 0;
                break;
            }
        }
        
        end:
        if(can)
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    return 0;
}

C. Mind Control

首先有一点可以确定:你控制你之后的人没有用,所以 \(k\) 最大为 \(m-1\)。然后我们也不用考虑具体怎么控制这些人。观察一下,不管怎么取,最后一定是剩下一个长度为 \(n-m+1\) 的原数组的子数组让你取,而这个子数组具体是哪个子数组,这要看两部分内容:

  • 你控制了多少个人取头部,人数我们记为 \(x\)
  • 你没法控制的人中,有多少个取了头部,人数我们记为 \(y\)

所以这个子数组就敲定下来了,因为你也只能取头部或者尾部,所以最后答案就是 \(max(a_{1+x+y},a_{1+x+y+(n-m)})\)

这样就有一个很显然的 \(O(n^2)\) 算法,双重循环遍历 \(x\)\(y\) 即可,我们记 \(b_i = \max(a_{1+i}, a_{1+i+(n-m)})\)

最终答案就是 \(\displaystyle \max_{x \in [0 , k]} \bigg[ \min_{y \in [0 , m-1-k]} b_{x+y} \bigg]\)

解释一下,最后的子数组你一定是取子数组两端的最大值,所以就是 \(b_{x+y}\),但是因为你不确定你没法控制的人都取了什么,你只能按最坏情况考虑,他们都在拆你的台,因此就有在 \(x\) 敲定后, \(y \in[0,m-1-k]\) 中你所能取到的 \(b_{x+y}\) 中的最小值,而在这些最小值中,因为 \(x\) 是你可以控制的,所以取这些最小值中的最大值就是答案。

我们将这个最终答案式等价变形为 \(\displaystyle \max_{x \in [0 , k]} \bigg[ \min_{y' \in [x , x+m-1-k]} b_{y'} \bigg]\) ,也就是令 \(y'=x+y\) ,这样换式子,问题就可以被更好的解释成 :对于数组 \(b\),长度为 \(m-k\)滑动数组中的最小值的最大值,滑动数组头部的取值为 \([0,k]\)

用线段树搞一搞就是 \(O(nlogn)\) 的解法,用单调队列搞一搞就是 \(O(n)\),代码是单调队列实现的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define _for(i,a,b) for(int i = (a);i < b;i ++)
#define _rep(i,a,b) for(int i = (a);i > b;i --)
#define INF 0x3f3f3f3f
#define ZHUO 11100000007
#define MOD 1000000007
#define MIKUNUM 39
#define pb push_back
#define debug() printf("Miku Check OK!\n")
#define maxn 3503
#define X first
#define Y second
int t;
int n, m, k;
int a[maxn];
 
int getb(int i)
{
    return max(a[1+i],a[1+i+(n-m)]);
}
 
int main()
{
    ios::sync_with_stdio(false);
    
    cin >> t;
    while(t--)
    {
        cin >> n >> m >> k;
        _for(i,1,n+1)
            cin >> a[i];
        
        k = min(k,m-1);
        
        deque<int> dq;
        int ans = -INF;
        
        for(int y = 0; y <= m-1;y ++)
        {
            //维护滑动数组长度
            while(dq.size() && dq.front() <= y-(m-k))
                dq.pop_front();
            //若当前不可能成为该滑动数组最小则出队
            while(dq.size() && getb(y) <= getb(dq.back()))
                dq.pop_back();
            dq.pb(y);
            //等滑动数组长度足够时再计算答案
            if(dq.size() && y >= m-k-1)
                ans = max(ans,getb(dq.front()));
        }
        printf("%d\n",ans);
    }
    return 0;
}

后面的题慢慢更

codeforces 1291 题解

原文:https://www.cnblogs.com/Asurudo/p/12258644.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!