Educational Codeforces Round 8

本文介绍了一种使用动态规划(DP)解决特定数值范围内的MagicNumbers计数问题的方法。通过状态设计dp(i,j,k)来表示考察前i位数、当前对m的余数为j且数值是否小于前缀的状态,有效地解决了计数问题。此外,还提供了完整的代码实现及另一题E.ZbaziinZeydabad的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Magic Numbers

看到计数取模就知道用dp了。。
首先把问题转换一下,变成求 1 ~x范围内满足要求的数有多少。然后设计状态 dp(i,j,k) i 表示考察了前i位数,j表示当前对 m 的余数,k表示小于前缀还是等于前缀(因为我们求的是 1 ~x,用这种方式来避免数超过 x )。状态转移见代码。
最后就是分别算b a 的结果,作差,并检查a是否合法。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const int mod = 1e9+7;

int m,d;
int len;
char stra[2010];
char strb[2010];
int a[2010];
int b[2010];

int dp[2010][2010][2];

int solve(int *num){
    memset(dp,0,sizeof(dp));

    for(int i=1;i<=num[0];i++){
        if(i==d)continue;
        if(i<num[0]){
            dp[0][i%m][1]++;
        }else{
            dp[0][i%m][0]++;
        }
    }

    for(int i=1;i<len;i++){
        for(int j=0;j<m;j++){
            if(i&1){
                dp[i][ (j*10+d)%m ][1] += dp[i-1][j][1];
                dp[i][ (j*10+d)%m ][1] %= mod;
                if(d<num[i]){
                    dp[i][ (j*10+d)%m ][1] += dp[i-1][j][0];
                    dp[i][ (j*10+d)%m ][1] %= mod;
                }else if(d==num[i]){
                    dp[i][ (j*10+d)%m ][0] += dp[i-1][j][0];
                    dp[i][ (j*10+d)%m ][0] %= mod;
                }
            }else{
                for(int k=0;k<10;k++){
                    if(k==d)continue;
                    dp[i][ (j*10+k)%m ][1] += dp[i-1][j][1];
                    dp[i][ (j*10+k)%m ][1] %= mod;
                    if(k<num[i]){
                        dp[i][ (j*10+k)%m ][1] += dp[i-1][j][0];
                        dp[i][ (j*10+k)%m ][1] %= mod;
                    }else if(k==num[i]){
                        dp[i][ (j*10+k)%m ][0] += dp[i-1][j][0];
                        dp[i][ (j*10+k)%m ][0] %= mod;
                    }
                }
            }
        }
    }

    int ans = dp[len-1][0][0]+dp[len-1][0][1];
    ans %= mod;
    return ans;
}

bool checkA(){
    ll cur = 0;
    for(int i=0;i<len;i++){
        cur*=10;
        cur+=a[i];
        cur%=m;
        if(i&1){
            if(a[i]!=d)return 0;
        }else{
            if(a[i]==d)return 0;
        }
    }
    return cur==0;
}

int main(){
    cin>>m>>d;
    scanf("%s",stra);
    scanf("%s",strb);
    len = strlen(stra);

    for(int i=0;i<len;i++){
        a[i]=stra[i]-'0';
        b[i]=strb[i]-'0';
    }

    int l=solve(a);
    int r=solve(b);

    ll ans = (r-l + checkA() +mod)%mod;
    cout<<ans<<endl;
    return 0;
}

E. Zbazi in Zeydabad

首先预处理出每个位置 (i,j) 开始往左,往右,往左下有多少个连续的z,分别记为 l(i,j) r(i,j) ld(i,j) 。然后按 i+j 递增(右上-左下对角线)的顺序枚举每个位置作为大z的右上角的情况。对于每个位置 (i,j) 而言,它能形成的大z至多为 min(l(i,j),ld(i,j)) ,再来考察z的下面那一“横”,根据 r(i,j) ,将向右足够长的行添加到BIT中维护,然后查询在 min(l(i,j),ld(i,j)) 范围内有多少行满足。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

char z[3010][3010];

int r[3010][3010];
int l[3010][3010];
int ld[3010][3010];

int c[6010];

inline int lowbit(int x){
    return x&(-x);
}

void update(int x){
    while(x<=6000){
        c[x]++;
        x+=lowbit(x);
    }
}

int query(int l,int r){
    int resl=0;
    l--;
    while(l){
        resl+=c[l];
        l-=lowbit(l);
    }
    int resr=0;
    while(r){
        resr+=c[r];
        r-=lowbit(r);
    }
    return resr-resl;
}


struct node{
    int row;
    int rr;
    node(){
    }
    node(int row,int rr):row(row),rr(rr){
    }
    bool operator<(const node &other)const{
        return rr>other.rr;
    }
};


int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        scanf("%s",z[i]+1);
    }

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(z[i][j]=='z'){
                l[i][j]=l[i][j-1]+1;
            }
        }
        for(int j=m;j>=1;j--){
            if(z[i][j]=='z'){
                r[i][j]=r[i][j+1]+1;
            }
        }
    }

    ll ans = 0;
    for(int sum=2;sum<=n+m;sum++){
        memset(c,0,sizeof(c));
        int i=min(sum-1,n);
        int j=sum-i;
        vector<node> vec;
        while(j<=m && i>=1){
            if(r[i][j])vec.push_back(node(i,j+r[i][j]-1));
            if(z[i][j]=='z'){
                ld[i][j] = ld[i+1][j-1]+1;
            }
            i--;
            j++;
        }
        sort(vec.begin(),vec.end());
        //
        j=min(sum-1,m);
        i=sum-j;
        int k = 0;
        while(i<=n && j>=1){
            while(k<vec.size() && vec[k].rr>=j){
                update(vec[k].row);
                k++;
            }
            int t = min(l[i][j],ld[i][j]);
            ans+=query(i,i+t-1);
            i++;
            j--;
        }
    }
    cout<<ans<<endl;
    return 0;
}
### Codeforces Educational Round 26 比赛详情 Codeforces是一个面向全球程序员的比赛平台,其中Educational Rounds旨在帮助参与者提高算法技能并学习新技巧。对于具体的Educational Round 26而言,这类比赛通常具有如下特点: - **时间限制**:每道题目的解答需在规定时间内完成,一般为1秒。 - **内存限制**:程序运行所占用的最大内存量被限定,通常是256兆字节。 - 输入输出方式标准化,即通过标准输入读取数据并通过标准输出打印结果。 然而,关于Educational Round 26的具体题目细节并未直接提及于提供的参考资料中。为了提供更精确的信息,下面基于以往的教育轮次给出一些常见的题目类型及其解决方案思路[^1]。 ### 题目示例与解析 虽然无法确切描述Educational Round 26中的具体问题,但可以根据过往的经验推测可能涉及的问题类别以及解决这些问题的一般方法论。 #### 类型一:贪心策略的应用 考虑一个问题场景,在该场景下需要照亮一系列连续排列的对象。假设存在若干光源能够覆盖一定范围内的对象,则可以通过遍历整个序列,并利用贪心的思想决定何时放置新的光源以确保所有目标都被有效照射到。这种情况下,重要的是保持追踪当前最远可到达位置,并据此做出决策。 ```cpp #include <bits/stdc++.h> using namespace std; bool solve(vector<int>& a) { int maxReach = 0; for (size_t i = 0; i < a.size(); ++i) { if (maxReach < i && !a[i]) return false; if (a[i]) maxReach = max(maxReach, static_cast<int>(i) + a[i]); } return true; } ``` #### 类型二:栈结构处理匹配关系 另一个常见问题是涉及到成对出现元素之间的关联性判断,比如括号表达式的合法性验证。这里可以采用`<int>`类型的栈来记录左括号的位置索引;每当遇到右括号时就弹出最近一次压入栈底的那个数值作为配对依据,进而计算两者间的跨度长度累加至总数之中[^2]。 ```cpp #include <stack> long long calculateParens(const string& s) { stack<long long> positions; long long num = 0; for(long long i = 0 ; i<s.length() ;++i){ char c=s[i]; if(c=='('){ positions.push(i); }else{ if(!positions.empty()){ auto pos=positions.top(); positions.pop(); num+=i-pos; } } } return num; } ``` #### 类型三:特定模式下的枚举法 针对某些特殊条件约束下的计数类问题,如寻找符合条件的三位整数的数量。此时可通过列举所有可能性的方式逐一检验是否符合给定规则,从而统计满足要求的结果数目。例如求解形如\(abc\)形式且不含重复数字的正整数集合大小[^3]。 ```cpp vector<int> generateSpecialNumbers(int n) { vector<int> result; for (int i = 1; i <= min(n / 100, 9); ++i) for (int j = 0; j <= min((n - 100 * i) / 10, 9); ++j) for (int k = 0; k <= min(n % 10, 9); ++k) if ((100*i + 10*j + k)<=n&&!(i==0||j==0)) result.emplace_back(100*i+10*j+k); sort(begin(result), end(result)); return result; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值