Educational Codeforces Round 123 (Rated for Div. 2) 简训

本文总结了 EducationalCodeforcesRound123 赛事中四个题目,涉及知识点包括思维策略、动态规划(DP)在ADoorsandKeys、BAnti-FibonacciPermutasion和CIncreaseSubarraySums中的应用,以及DCrossColoring的巧妙解法。通过实例演示和关键思路,帮助读者提升编程技能和算法理解。

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

导语

我真菜
在这里插入图片描述

涉及的知识点

思维,dp

链接:Educational Codeforces Round 123 (Rated for Div. 2)

题目

A Doors and Keys

题目大意:三扇门,每一扇有一个颜色,只有先拿到对应颜色的钥匙才能开门,给出钥匙和门的次序判断是否能把门全开了

思路:直接判断即可

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n;
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        char s[10];
        cin >>s;
        bool r=0,g=0,b=0,f=1;
        for(int i=0; i<6; i++) {
            if(s[i]=='r')r=1;
            else if(s[i]=='g')g=1;
            else if(s[i]=='b')b=1;
            if(s[i]=='R')
                if(r)continue;
                else {
                    cout <<"NO\n";
                    f=0;
                    break;
                }
            if(s[i]=='G')
                if(g)continue;
                else {
                    cout <<"NO\n";
                    f=0;
                    break;
                }
            if(s[i]=='B')
                if(b)continue;
                else {
                    cout <<"NO\n";
                    f=0;
                    break;
                }
        }
        if(f)cout <<"YES\n";
    }
    return 0;
}

B Anti-Fibonacci Permutasion

题目大意:定义反斐波那契数列:任意两个连续下标对应的元素加起来不等于下一个元素,从1 - n全排列中找出n个反斐波那契数列

思路:如果直接全排列逐个循环左移的话,能够构造出n-1个(因为第一个不行),由于1 2 3这三个数字的干扰,直接预先交换得到1 3 2,然后循环左移即可,需要特判3

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int t,n,a[100];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n;
        if(n==3) {//特判3
            cout <<"3 2 1\n1 3 2\n3 1 2\n";
            continue;
        }
        for(int i=1; i<=n; i++)
            a[i]=i;
        swap(a[3],a[2]);//直接交换
        for(int j=1; j<=n; j++)
            cout <<a[j]<<" ";
        cout <<endl;
        //swap(a[3],a[2]);
        for(int i=2; i<=n; i++) {//逐步循环左移
            int tmp=a[1];
            for(int i=1; i<=n-1; i++)
                a[i]=a[i+1];
            a[n]=tmp;
            for(int j=1; j<=n; j++)
                cout <<a[j]<<" ";
            cout <<endl;
        }
    }
    return 0;
}

C Increase Subarray Sums

题目大意:给出一个整数序列 a a a,给出一个整数 x x x,定义函数 f ( k ) f(k) f(k)如下:对序列 a a a中任意 k k k个不同的元素都加上 x x x之后能够获得的最大连续子序列和,空序列也被认为合法(和为0),计算 k k k从0到n的所有 f ( k ) f(k) f(k)

思路:考虑dp, d p [ i ] [ j ] dp[i][j] dp[i][j]为前i个数进行j次操作能够获得的最大值,如果不考虑负数的话,可以得到转移方程如下

  1. d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + a [ i ] dp[i][j]=dp[i-1][j]+a[i] dp[i][j]=dp[i1][j]+a[i]
  2. d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + a [ i ] + x dp[i][j]=dp[i-1][j-1]+a[i]+x dp[i][j]=dp[i1][j1]+a[i]+x

但是由于有负数的存在,那么每次操作就需要慎重考虑,是否需要舍弃前面的序列而直接从当前数开始构造,那么就可以得到更新之后的方程

  1. d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , 0 ) + a [ i ] dp[i][j]=max(dp[i-1][j],0)+a[i] dp[i][j]=max(dp[i1][j],0)+a[i]
  2. d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − 1 ] , 0 ) + a [ i ] + x dp[i][j]=max(dp[i-1][j-1],0)+a[i]+x dp[i][j]=max(dp[i1][j1],0)+a[i]+x

这里的max是为了消除前面序列的负数的影响

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
int n,t,x,dp[5252][5252],a[5252],ans[5252];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n>>x;
        for(int i=1; i<=n; i++) {
            cin >>a[i];
            ans[i]=0;
        }
        ans[0]=0;//初始化答案存储
        for(int i=0; i<=n; i++)//初始化Dp
            for(int j=0; j<=n; j++)dp[i][j]=0;
        for(int i=1; i<=n; i++) {
            dp[i][0]=max(dp[i-1][0],0*1ll)+a[i];
            ans[0]=max(ans[0],dp[i][0]);
        }
        for(int i=1; i<=n; i++) {
//            dp[i][0]=max(dp[i-1][0],0*1ll)+a[i];
//            ans[0]=max(ans[0],dp[i][0]);
            for(int j=1; j<=n; j++) {
                //int l=dp[i][j-1]+x;由于是以i为基础的,不能这么写
                int l=max(dp[i-1][j-1],0*1ll)+a[i]+x;
                int r=max(dp[i-1][j],0*1ll)+a[i];
                dp[i][j]=max(l,r);
                ans[j]=max(ans[j],dp[i][j]);
            }
        }
        for(int i=0; i<=n; i++)
            cout <<ans[i]<<" ";
        cout <<endl;
    }
    return 0;
}

D Cross Coloring

题目大意:给出一共n×m的矩阵,每个位置初始都是白色,一共有q次操作,每次操作给出一个坐标 x i , y i x_i,y_i xi,yi,选择一个非白色的颜色将 x i x_i xi行和 y i y_i yi列涂上这个颜色,q次操作之后被认为是一次涂色完成,只要有一个格子颜色不一样就会认为是涂色方案不一样,求一共有多少种涂色方案

思路:思路和POJ2528有点像,对于每一个格子来说,只有最后一次操作是有效的,因为之前的操作都被覆盖了,那么可以逆向思维,直接从最后一次操作开始巨鹿,如果当前操作可以唯一决定至少一个位置的颜色,那么这次操作就有效,可以选择k种颜色,否则就无效
判断当前操作是否可以唯一决定至少一个位置,有三种情况:所有行已染色,所有列已染色,x行y列已经染色(都相当于后来的操作覆盖了前面的操作),直接判断即可

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int maxn=2e5+5;
int t,n,m,k,q,res,x[maxn],y[maxn],sumr,sumc;
bool row[maxn],col[maxn];
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >>t;
    while(t--) {
        cin >>n>>m>>k>>q;
        memset(row,0,sizeof(row));
        memset(col,0,sizeof(col));
        sumc=sumr=0;//初始化变量
        res=1;
        for(int i=1; i<=q; i++)//录入操作
            cin >>x[i]>>y[i];
        for(int i=q; i>=1; i--) {//倒着来,因为不管先前操作怎样,都会被最后一次操作覆盖
            if(sumr==n||sumc==m||(row[x[i]]&&col[y[i]]))continue;
            //如果倒序操作已经做到了全覆盖或者目标覆盖
            if(!row[x[i]]) {//如果未覆盖当前行
                row[x[i]]=1;
                sumr++;
            }
            if(!col[y[i]]) {//如果未覆盖当前列
                col[y[i]]=1;
                sumc++;
            }
            res=1ll*k*res%mod;
        }
        cout <<res<<endl;
    }
    return 0;
}

参考文献

  1. Increase Subarray Sums(dp/前缀和)
  2. Cross Coloring(思维/map/逆向)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值