Codeforces 成长之路 #689 div2 题解

比赛地址

https://codeforces.ml/contest/1461

A. String Generation

题目大意

输出一个只能由小写字母a、b、c组成的长度为n且最长子回文串长度不超过k的序列。

解题思路

直接按照abcabc轮流输出即可,这时候最长子回文串长度为1。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
    int n,k;cin>>n>>k;
    for(int i=1;i<=n;i++)
        if(i%3==0)
            cout<<'a';
        else if(i%3==1)
            cout<<'b';
        else if(i%3==2)
            cout<<'c';
    cout<<endl;
}
int main()
{
   int T;cin>>T;
    while(T--)
        solve();
}

B. Find the Spruce

题目大意

给你一个n*m矩阵,由.和*组成,其中*为树的叶子。问矩阵种能组成树的最多数量是多少。树如图下所示,一个小黑块等于一个*。
在这里插入图片描述

解题思路

数据范围才500,可以用n^3的暴力解决。比赛时本人写了个动态规划,但是注意使用short和bool,不然会MLE。其中 dp[i][j] 代表矩阵中 (i,j) 向前有多少个连续的*,is[i][j][k] 代表 (i,j) 之前的k个元素是一棵树的底层。然后我们只要枚举最下面一层的大小,假设为k,则状态转移方程为
if(dp[i][j]>=k&&is[i-1][j-1][k-2])
ans++,is[i][j][k]=1;
含义是,(i,j) 向前有至少k个连续的*,而且上一层的 (i-1,j-1) 向前的k-2个元素是一棵树的最底层,那么第 (i,j) 向前的k个连续* 可以拼凑在上面一棵树之下作为新的底层形成一颗新的树。

AC代码

#include<bits/stdc++.h>
#define MAXN 505
using namespace std;
typedef long long ll;
short dp[MAXN][MAXN];
bool is[MAXN][MAXN][MAXN];
char mmap[MAXN][MAXN];
void clr(int n,int m)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            dp[i][j]=0;
            for(int k=1;k<=m;k++)
                is[i][j][k]=0;
        }
}
void solve()
{
    int n,m;cin>>n>>m;
    ll ans=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>mmap[i][j];
            if(mmap[i][j]=='*')
            {
                is[i][j][1]=dp[i][j]=1;
                if(mmap[i][j-1]=='*')
                    dp[i][j]+=dp[i][j-1];
                ans++;
            }
        }
    for(int k=3;k<=m;k+=2)
        for(int i=2;i<=n;i++)
            for(int j=1;j<=m;j++)
            if(dp[i][j]>=k&&is[i-1][j-1][k-2])
                ans++,is[i][j][k]=1;
    cout<<ans<<endl;
    clr(n,m);
}
int main()
{
   int T;cin>>T;
    while(T--)
        solve();
}

C. Random Events

题目大意

给你一个1~n的随机序列,再给你n次操作,每次操作有一定概率p触发,操作的方式是对0 ~ r 进行排序,问你整个序列最终变成有序的概率是多少。

解题思路

比较简单的数学题。假设从0~m是无序序列,m+1到n是有序序列,那我们需要知道至少有一次 r>=m 的操作的发生的概率就是我们要求的概率。如果序列本身就是有序的,则概率为1。

AC代码

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
int a[MAXN];
int dp[MAXN];
void solve()
{
    int n,m;cin>>n>>m;
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
        cin>>a[i];
    double np=1;
    for(int i=n;i>=1&&a[i]==i;i--)
        dp[i]=1;
    dp[n+1]=1;
    for(int i=1;i<=m;i++)
    {
        int aa;double b;
        cin>>aa>>b;
        if(dp[aa+1]) //说明这次操作的r>=m
            np*=(1-b);
    }
    np=1-np;
    if(dp[1])
        np=1;
    printf("%.8f\n",np);;
}
int main()
{
   int T;cin>>T;
    while(T--)
        solve();
}

D. Divide and Summarize

题目大意

给你一个数组,设mid=(数组中最大元素+最小元素)/2,你可以把小于等于mid的数放在一堆,大于mid的数放在一堆形成两个新的数组。同样,新形成的两个数组也可以进行相同操作。给你一堆询问,每次询问给出一个数,问是否有通过这样方式构造的数组的元素和等于这个数。

解题思路

直接排序以后二分模拟他的操作,并且把所有可能的值存下来,计算数组和的时候可以用前缀和差分数组O(1)计算,最后对着记录下来的答案输出询问。听说unordered_map在比赛中有可能被hack,所以建议以后老老实实用logn的map。

AC代码

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
typedef long long ll;
unordered_map<ll,bool> um;
int a[MAXN];
ll premix[MAXN];
void func(int l,int r)
{
    if(l>=r) return;
    int mid=(a[l]+a[r])/2;
    int pos=upper_bound(a+l,a+r+1,mid)-a;
    if(pos==r+1)return;
    um[premix[pos-1]-premix[l-1]]=1;
    um[premix[r]-premix[pos-1]]=1;
    func(l,pos-1);
    func(pos,r);
}
void solve()
{
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
        premix[i]=premix[i-1]+a[i];
    um[premix[n]]=1;
    func(1,n);
    for(int i=1;i<=m;i++)
    {
        int t;cin>>t;
        cout<<(um[t]?"YES":"NO")<<endl;
    }
    um.clear();
}
int main()
{
   int T;cin>>T;
    while(T--)
        solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值