Educational Codeforces Round 147

发布时间 2023-04-24 19:55:10作者: cryingrain

A

题意

思路

有前导零结果直接为0,出现在第一位的贡献为9,其他地方的贡献为10

代码

#include<bits/stdc++.h>
 
using namespace std;
using ll=long long;
 
char s[10];
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%s",s);
        if(s[0]=='0')
        {
            printf("0\n");
            continue;
        }
        int res=s[0]=='?'?9:1;
        for(int i=1;i<strlen(s);i++)
            if(s[i]=='?')res*=10;
        printf("%d\n",res);
    }
}

B

题意

给一个数组,对其进行一次如下操作:选一个区间,然后将这个区间进行从小到大排序。现在给出操作前和操作后的数组,求可能选择的最长区间是哪里。

思路

刚开始想到可以直接找最长不降子串,但是实际上区间不一定是最长不降子串,因为有可能数组中本身存在一个长的不降区间,但是并没有选择这个区间操作。

所以想到可以先找到一个区间[l,r],使得l,r位置修改前后的值不相等,然后再在修改后的数组中,向左向右扩展,扩展到可以得到的最大不降子串。因为可以等价于选择了扩展后的区间操作。

代码

#include<bits/stdc++.h>
 
using namespace std;
 
const int MAX=2e5+5;
 
int a[MAX],b[MAX];
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        for(int i=0; i<n; i++)
            scanf("%d",&a[i]);
        for(int i=0; i<n; i++)
            scanf("%d",&b[i]);
       int l=0,r=n-1;
       while(a[l]==b[l])l++;
       while(a[r]==b[r])r--;
       while(l>0&&b[l-1]<=b[l])l--;
       while(r<n-1&&b[r+1]>=b[r])r++;
       printf("%d %d\n",l+1,r+1);
    }
}

C

题意

给一个小写字母组成的字符串,每次可以删除其中的任意字符,但是选择删除的字符位置不能相邻。求最少多少次操作可以使得字符串只剩下相同的某个字母。

思路

首先可以想到,操作一定有一个上限,那就是每次选择奇数或偶数位置的字符删除,最后留下一个字母,设字符串长度为length,操作次数的上限为

\[\lfloor log_2(length) \rfloor \]

然后可以想到,由于总共只有26个字母,那么可以枚举最后剩下哪个字母,其他的全部删除。枚举留下的字母,然后以当前字母为分隔符,将字符串分块,将各块全部删除,操作次数就是删除最长的分块需要的操作次数。根据上述的结论,这个操作次数为

\[\lfloor log_2(length) \rfloor+1 \]

因为要把最后剩的一个字符也删除。

代码

#include<bits/stdc++.h>
 
using namespace std;
 
const int MAX=2e5+5;
 
char s[MAX];
vector<int>pos[26];
 
int left1(int n)
{
    int res=28;
    while(((1<<res)&n)==0)res--;
    return res+1;
}
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        for(int i=0;i<26;i++)
            pos[i].clear();
        scanf("%s",s);
        int n=strlen(s),minn=n;
        for(int i=0;i<n;i++)
            pos[s[i]-'a'].push_back(i);
        for(int i=0;i<26;i++)
        {
            int last=-1,maxx=0;
            pos[i].push_back(n);
            for(int j=0;j<pos[i].size();j++)
            {
                maxx=max(maxx,pos[i][j]-last-1);
                last=pos[i][j];
            }
            minn=min(minn,maxx);
        }
        if(minn==0){printf("0\n");continue;}
        printf("%d\n",left1(minn));
    }
}

D

题意

给一个1*inf的格子条,以及一系列区间,区间互不相连,从左到右移动,每次在区间内,可以按下按钮开始染色,然后松开按钮停止染色,区间外不能染色。每移动一格,代价为1,按下和松开按钮代价也均为1。求最少代价,使得总共染色格子数量为k

思路

决策的点就在于要不要跳过一些区间,由于区间不连续,那么从一个区间末端到下一个区间起点最少需要2的代价。然后很明显可以想到,只要区间长度>=2,那么就没必要跳过,因为如果跳过任意一个长度>=2的区间,那么尽管减少了按下和松开按钮的代价(共为2),但是收益也至少减少了2,那么在之后的区间中就必须至少额外染色2个格子,相当于代价增加了2,因此跳过不会带来收益。

只有跳过长为1的区间会带来收益,所以可以想到统计长为1的区间个数,遍历区间,每遇到染色格子数量>=k时,就尝试用当前的区间额外长度替换之前的长为1的区间,可以减少按按钮的代价,最后整体取最优解。

#include<bits/stdc++.h>
 
using namespace std;
 
const int MAX=2e5+5;
 
int l[MAX],r[MAX];
 
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,k;
        scanf("%d%d",&n,&k);
        int cur=0,ans=2e9+7,cnt1=0;
        for(int i=0;i<n;i++)
            scanf("%d",&l[i]);
        for(int i=0;i<n;i++)
            scanf("%d",&r[i]);
        for(int i=0;i<n;i++)
        {
            int len=(r[i]-l[i]+1);
            if(len==1)cnt1++;
            cur+=len;
            if(cur>=k)
            {
                int remain=cur-k;
                int mincnt=min(cnt1,remain);
                ans=min(ans,(i+1-mincnt)*2+r[i]-remain+mincnt);
            }
        }
        if(ans==2e9+7)ans=-1;
        printf("%d\n",ans);
    }
}