【Educational Codeforces Round 53 (Rated for Div. 2)】

前言

好 久 不 打 c f , 为 了 晚 上 的 e d u 自 己 开 了 一 场 v p 好久不打cf,为了晚上的edu自己开了一场vp cfeduvp
战 绩 还 算 可 以 , 只 是 好 久 不 打 前 期 题 卡 的 时 间 有 点 长 战绩还算可以,只是好久不打前期题卡的时间有点长
但 是 看 榜 D 最 开 始 过 的 多 就 去 写 D , 发 现 就 是 个 简 单 模 拟 , 由 于 b r e a k 写 少 w a 了 一 发 之 后 就 过 了 但是看榜D最开始过的多就去写D,发现就是个简单模拟,由于break写少wa了一发之后就过了 DDbreakwa
回 来 写 c 发 现 是 个 码 力 题 , 码 码 码 就 过 了 。 回来写c发现是个码力题,码码码就过了。 c
刷 专 题 对 打 c f 的 提 升 还 是 很 大 的 , 下 一 年 争 取 打 满 c f 吧 刷专题对打cf的提升还是很大的,下一年争取打满cf吧 cfcf O(∩_∩)O

无惧掉分


A. Diverse Substring

题意

求 一 个 长 度 为 n 的 字 符 串 是 否 存 在 一 个 子 串 , 满 足 子 串 中 出 现 最 多 次 数 的 字 符 出 现 次 数 < = l e n / 2 求一个长度为n的字符串是否存在一个子串,满足子串中出现最多次数的字符出现次数<=len/2 n<=len/2
n < = 1 e 3 n<=1e3 n<=1e3

做法

直 接 找 是 否 有 两 个 连 续 的 不 同 的 字 符 , 输 出 就 可 以 直接找是否有两个连续的不同的字符,输出就可以

坑点

直 接 暴 力 枚 举 所 有 子 串 维 护 一 个 出 现 次 数 最 多 的 字 符 的 出 现 次 数 就 好 直接暴力枚举所有子串维护一个出现次数最多的字符的出现次数就好
简 单 题 想 不 到 好 做 法 的 时 候 直 接 暴 力 简单题想不到好做法的时候直接暴力

代码

#include
#include
#include
#include
using namespace std;
const int maxn = 1e5+10;
char str[maxn];
int sum[30];
int main()
{
    int len;
    scanf("%d%s",&len,str);
    for(int i=1;i<len;i++)
    {
        if(str[i]!=str[i-1])
        {
            printf("YES\n");
            cout<<str[i-1]<<str[i]<<endl;
            return 0;
        }
    }
    printf("NO\n");
    return 0;
}

B. Vasya and Books

题意

给 你 一 些 书 , 每 本 书 有 自 己 的 编 号 a [ i ] , 每 本 书 从 下 到 上 叠 在 一 起 给你一些书,每本书有自己的编号a[i],每本书从下到上叠在一起 a[i],
然 后 每 次 你 要 买 编 号 为 b [ i ] 的 书 和 他 上 面 得 所 有 书 , 输 出 每 次 能 买 到 多 少 本 书 然后每次你要买编号为b[i]的书和他上面得所有书,输出每次能买到多少本书 b[i]
n < = 2 ∗ 1 0 5 n<=2*10^5 n<=2105

做法

类 似 单 调 栈 的 思 想 , 如 果 之 前 买 过 比 当 前 要 买 的 书 深 度 更 深 的 , 那 么 这 次 就 什 么 都 买 不 到 类似单调栈的思想,如果之前买过比当前要买的书深度更深的,那么这次就什么都买不到
所 以 我 们 只 需 要 维 护 一 个 深 度 最 大 值 , 每 次 只 买 深 度 最 大 值 与 当 前 深 度 之 间 的 书 , 并 更 新 深 度 最 大 值 所以我们只需要维护一个深度最大值,每次只买深度最大值与当前深度之间的书,并更新深度最大值

代码

#include
#include
#include
#include
using namespace std;
const int maxn = 2e5+10;
int a[maxn],b[maxn];
int mp[maxn];
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        mp[a[i]]=i;
    }
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    int maxx=0;
    for(int i=1;i<=n;i++)
    {
        int tmp=mp[b[i]];
        printf("%d ",max(0,tmp-maxx));
        maxx=max(maxx,tmp);
    }
    return 0;
}

C. Vasya and Robot

题意

在 二 维 平 面 上 有 一 个 机 器 人 最 开 始 在 点 ( 0 , 0 ) 处 在二维平面上有一个机器人最开始在点(0,0)处 (0,0)
最 终 他 要 走 到 点 ( x , y ) 处 , 现 在 给 出 行 动 路 线 , 有 ( L , R , U , D ) 四 种 走 法 最终他要走到点(x,y)处,现在给出行动路线,有(L,R,U,D)四种走法 (x,y),线(L,R,U,D)
每 次 他 可 以 修 改 某 个 区 间 内 的 走 法 , 问 需 要 修 改 的 最 小 区 间 长 度 是 多 少 每次他可以修改某个区间内的走法,问需要修改的最小区间长度是多少
( 区 间 内 可 以 修 改 为 任 意 走 法 , 也 可 以 不 修 改 ) (区间内可以修改为任意走法,也可以不修改)
n < = 2 ∗ 1 0 5 n<=2*10^5 n<=2105

做法

这 个 数 据 范 围 大 概 只 能 想 二 分 , 二 分 长 度 之 后 枚 举 每 个 点 作 为 区 间 起 点 是 否 能 走 到 ( x , y ) 这个数据范围大概只能想二分,二分长度之后枚举每个点作为区间起点是否能走到(x,y) (x,y)
c h e c k 的 过 程 就 是 先 把 区 间 之 外 的 走 法 走 完 , 得 到 ( x 1 , y 1 ) check的过程就是先把区间之外的走法走完,得到(x1,y1) check(x1,y1)
再 判 断 ( x 1 , y 1 ) 与 ( x , y ) 的 曼 哈 顿 距 离 D 再判断(x1,y1)与(x,y)的曼哈顿距离D (x1,y1)(x,y)D
如 果 当 前 区 间 长 度 l e n > D 而 且 ( l e n − D ) M o d 2 = = 0 , 就 代 表 机 器 人 可 以 通 过 当 前 区 间 走 到 ( x , y ) 如果当前区间长度len>D而且(len-D)Mod2==0,就代表机器人可以通过当前区间走到(x,y) len>DlenD)Mod2==0(x,y)
代码

#include
#include
#include
#include
using namespace std;
#define dbg(x) cout<<#x<<" = "<
const int maxn = 2e5+10;
char s[maxn];
int x,y;
int n,sum[maxn][4];//U D L R
map<char,int> mp;
int n1,n2,n3,n4;
bool check(int mid)
{
    int sum1=0,sum2=0,sum3=0,sum4=0;
    for(int i=1;i<=n-mid+1;i++)
    {
        sum1=sum[i-1][0]+sum[n][0]-sum[i+mid-1][0];
        sum2=sum[i-1][1]+sum[n][1]-sum[i+mid-1][1];
        sum3=sum[i-1][2]+sum[n][2]-sum[i+mid-1][2];
        sum4=sum[i-1][3]+sum[n][3]-sum[i+mid-1][3];
        int xx=0,yy=0;
        n1=0,n2=0,n3=0,n4=0;
        xx+=(sum4-sum3);
        yy+=(sum1-sum2);
        if(xx>=x) n3=xx-x;
        else n4=x-xx;
        if(yy>=y) n2=yy-y;
        else n1=y-yy;
        if(n1+n2+n3+n4<=mid&&(mid-n1-n2-n3-n4)%2==0) return true;
    }
    return false;
}
int main()
{
    mp['U']=0;
    mp['D']=1;
    mp['L']=2;
    mp['R']=3;
    scanf("%d%s",&n,s+1);
    for(int i=1;i<=n;i++)
    {
        sum[i][0]=sum[i-1][0];
        sum[i][1]=sum[i-1][1];
        sum[i][2]=sum[i-1][2];
        sum[i][3]=sum[i-1][3];
        sum[i][mp[s[i]]]++;
    }
    scanf("%d%d",&x,&y);
    int l=0,r=n,mid;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid)) r=mid-1;
        else l=mid+1;
    }
    if(l==n+1) printf("-1\n");
    else printf("%d\n",l);
    return 0;
}

D. Berland Fair

题意

有 n 个 商 品 排 列 成 一 行 , 每 个 商 品 有 一 个 加 个 a [ i ] , 最 初 你 身 上 有 T 元 有n个商品排列成一行,每个商品有一个加个a[i],最初你身上有T元 na[i]T
每 次 都 从 左 到 右 走 , 如 果 买 的 起 这 个 商 品 就 买 一 件 , 买 不 起 就 不 买 每次都从左到右走,如果买的起这个商品就买一件,买不起就不买
每 次 走 到 头 就 重 新 从 左 端 走 , 问 最 后 能 买 到 多 少 件 商 品 每次走到头就重新从左端走,问最后能买到多少件商品
n < = 2 ∗ 1 0 5      1 < = T < = 1 0 18 n<=2*10^5 \ \ \ \ 1<=T<=10^{18} n<=2105    1<=T<=1018

做法

由 于 T 比 较 大 , 我 们 肯 定 要 用 到 除 法 由于T比较大,我们肯定要用到除法 T
每 次 我 们 统 计 当 前 可 买 的 商 品 的 价 值 总 和 n o w 每次我们统计当前可买的商品的价值总和now now
若 T > n o w , 则 下 次 遍 历 一 次 所 有 商 品 还 是 这 么 买 若T>now,则下次遍历一次所有商品还是这么买 T>now,
否 则 我 们 就 重 新 判 断 当 前 可 以 购 买 哪 些 商 品 否则我们就重新判断当前可以购买哪些商品
但 是 我 们 要 考 虑 所 有 商 品 都 小 于 T , 但 是 不 能 一 起 购 买 的 数 据 但是我们要考虑所有商品都小于T,但是不能一起购买的数据 T
我 们 就 要 从 前 往 后 模 拟 一 遍 更 新 一 次 T 我们就要从前往后模拟一遍更新一次T T
再 重 新 统 计 所 有 小 于 T 的 商 品 的 价 值 和 再重新统计所有小于T的商品的价值和 T
最 后 直 到 一 个 物 品 也 购 买 不 了 退 出 循 环 最后直到一个物品也购买不了退出循环 退

代码

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
ll a[maxn];
int main()
{
    int n;
    ll T;
    scanf("%d%lld",&n,&T);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    ll ans=0;
    while(T)
    {
        ll sum=0;
        ll cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i]<=T)
            {
                cnt++;
                sum+=a[i];
            }
        }
        if(cnt==0) break;
        if(T<sum)
        {
            for(int i=1;i<=n;i++)
            {
                if(T>=a[i])
                {
                    ans++;
                    T-=a[i];
                }
            }
        }
        else
        {
            ans+=1LL*cnt*(T/sum);
            T=T%sum;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

E. Segment Sum

题意

题 意 很 简 单 , 求 l 到 r 之 间 的 所 有 数 中 不 同 数 位 数 不 超 过 k 的 数 之 和 题意很简单,求l到r之间的所有数中不同数位数不超过k的数之和 lrk
1 < = l < = r < = 1 0 18    1 < = k < = 10 1<=l<=r<=10^{18} \ \ 1<=k<=10 1<=l<=r<=1018  1<=k<=10

做法

一 眼 就 知 道 是 数 位 d p 一眼就知道是数位dp dp
而 且 我 们 也 很 容 易 想 到 状 态 的 定 义 而且我们也很容易想到状态的定义
数 位 d p 时 要 往 下 传 递 都 有 哪 些 数 位 出 现 过 , 这 个 状 态 是 ( 1 < < 10 ) 的 数位dp时要往下传递都有哪些数位出现过,这个状态是(1<<10)的 dp(1<<10)
由 于 我 们 要 统 计 和 , 每 个 状 态 要 保 存 两 个 值 由于我们要统计和,每个状态要保存两个值
满 足 状 态 的 数 字 个 数 和 当 前 状 态 数 字 对 答 案 的 贡 献 满足状态的数字个数和当前状态数字对答案的贡献

坑点

由 于 本 题 中 前 导 0 对 状 态 有 影 响 , 注 意 数 位 d p 时 前 导 0 的 问 题 由于本题中前导0对状态有影响,注意数位dp时前导0的问题 0dp0

代码

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const ll Mod = 998244353ll;

typedef pair<ll,ll> pll;
#define Se second
#define Fi first
ll l,r,k;
ll pow_[20];
int cal(ll x)
{
    int ans=0;
    while(x)
    {
        if(x&1) ans++;
        x/=2;
    }
    return ans;
}
pll dp[20][1<<12][2];
ll a[20];
pll dfs(ll len,ll state,bool limit,bool lead)
{
    if(len==0) return pll(1,0);//遍历结束,无需算对答案的贡献,但是要像正常数位dp一样统计满足条件的个数
    if(!limit&&dp[len][state][lead].Se) return dp[len][state][lead];//注意带前导0的数位dp套路
    pll ans=pll(0,0);
    int up=limit?a[len]:9;
    for(int i=0;i<=up;i++)
    {
        ll temp=state|((lead||i)<<i);
        //若当前有前导0而且当前数位为0,则状态不更新
        if(cal(temp)>k) continue;
        //不满足答案的状态要去掉
        pll tmp=dfs(len-1,temp,limit&&i==a[len],lead||i);
        ans.Fi=(ans.Fi+tmp.Fi)%Mod;
        ans.Se=(ans.Se+tmp.Se+(1LL*i*pow_[len-1]%Mod*tmp.Fi)%Mod)%Mod;
        //当前数位对答案的贡献为当前数位所代表的值为i*pow[len-1]
        //当前数位为i的数字个数为tmp.Fi
        //总贡献为:i*pow_[len-1]%Mod*tmp.Fi
    }
    return dp[len][state][lead]=ans;
}
ll solve(ll x)
{
    for(int i=0;i<20;i++)
    {
        for(int j=0;j<(1<<12);j++)
        {
            for(int l=0;l<2;l++)
            {
                dp[i][j][l].Fi=0;
                dp[i][j][l].Se=0;
            }
        }
    }
    memset(a,0,sizeof(a));
    int len=0;
    while(x)
    {
        a[++len]=x%10;
        x/=10;
    }
    return dfs(len,0,true,0).Se%Mod;
}
int main()
{
    pow_[0]=1;
    for(int i=1;i<20;i++) pow_[i]=pow_[i-1]*10%Mod;//每一位的贡献预处理
    scanf("%lld%lld%lld",&l,&r,&k);
    printf("%lld\n",(solve(r)-solve(l-1)+Mod)%Mod);
    return 0;
}

你可能感兴趣的:(Codeforces,个人训练计划)