Educational Codeforces Round 112 (Rated for Div. 2)A~D题解

A、PizzaForces

题意:

给定一个n,让你求制作至少n个披萨所需的时间。其中,每次制作6、8、10个披萨时间分别为15、20、25分钟,。

思路:

我们很容易发现制作每个披萨的时间都为2.5分钟,且每次只能制作偶数个披萨,所以即使n为奇数,我们也只能制作偶数个披萨。然后我们注意到 从6开始的偶数的披萨数,我们都可以 用题目给定的三种制作方式,拼凑出来,例如 12= 6+6,14=6+8,16=8+8…以此类推。所以答案就显而易见了,注意要特判:当n<6时,我们也只能制作6个披萨。若n为奇数,我们让其加1,然后乘2.5即是其所需的总时间了,若为偶数,则直接乘2.5即可。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std; 
#define int long long
#define fi first
#define se second
#define endl '\n'
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
const int N=1e6+5;
int h[N],e[N],nx[N],idx;
int k,T,t,n,m,ans,cnt;
int a[N];
bool vis[N];
priority_queue <int,vector<int>,greater<int> > q;
signed main()
{
        ios::sync_with_stdio(false);
         cin.tie(0),cout.tie(0);
        cin>>T;
        while(T--)
        {
                cin>>n;
                if(n<=6)
                {
                        cout<<15<<endl;
                        continue;
                }
                if(n%2) n++;
                ans=n*15/6;
                cout<<ans<<endl;
                
        }

        return 0;
}

B、Two Tables

题意:

给定一个房间的长和宽,以及第一个桌子摆放的位置,还有第二个桌子的长和宽。让你判断能否通过移动第一个桌子,把第二个桌子放入房间中。若能成功放入,则输出第一个桌子移动的最短距离,若不能,则输出-1。(注意桌子不能旋转)

思路:

本题感觉写起来像C语言考试,没什么难度,只要想清楚 若要最小化移动第一张桌子的距离,最好的办法就是横竖移动,而不是斜着移动(题目给的图和要求误差不超过1e-6是老诈骗了)。
我们一开始先进行判断,如果把两个桌子一起横着放或者竖着放都不能放下去的话,那直接输出-1即可(因为如果满足其中一个,我们就可以把第二桌子用那种方式和第一张桌子放在一起)。
如果两张桌子都能放下的话,我们应该怎么求最小值呢?这里以横着放为例,若能横着放的话,其实只要求第一涨桌子要向左或右移动多少距离 才可以让第二张桌子能放在房子中,这里取向左或向右移动的最小距离即可。竖着移动也同理。那么我们只要取所有上下左右移动的最小距离即是我们所需的答案了(前提是能移动)。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std; 
#define int long long
#define fi first
#define se second
#define endl '\n'
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
const int N=1e6+5;
int h[N],e[N],nx[N],idx;
int k,T,t,n,m,ans,cnt;
int a[N];
bool vis[N];
int x1,y1,x2,y2,yy,xx,dx,dy;
priority_queue <int,vector<int>,greater<int> > q;
void solve()
{
        int res=INF;
        int down_y=yy-y1,up_y=y2+yy-m;
        int down_x=xx-x1,up_x=x2+xx-n;
        if(down_y<=0||up_y<=0||down_x<=0||up_x<=0)
        {
                cout<<0<<endl;                
                return;
        }
        if(yy+dy<=m) res=min(res,min(up_y,down_y));
        if(xx+dx<=n) res=min(res,min(down_x,up_x));                                        
        cout<<res<<endl;
}
signed main()
{
        cin>>T;
        while(T--)
        {
                cin>>n>>m;
                cin>>x1>>y1>>x2>>y2;
                cin>>xx>>yy;                
                dx=abs(x1-x2),dy=abs(y1-y2);
                if(dx+xx>n&&dy+yy>m)
                {
                        cout<<-1<<endl;
                        continue;
                }
                solve();
        }
        return 0;
}

C、Coin Rows

题意:

给定一个2*n的地图,并且每个点上都有若干硬币。Alice和Bob都从点(1,1)走到点(2,n),Alice先走,Bob后走(并且两人只能向下或向右走,不能回头),Alice会把所走过的点上的硬币取走,并且Alice要选择 能最小化Bob所能获得的硬币个数 的那条路走(Ailce自己能获得多少硬币无所谓,只要能恶心到Bob就可以了)。而Bob会选择最优走法,去拾取 已经被Alice拾取过后的地图 中最多的硬币。让你求通过Alice的损人不利己的操作,Bob最多能获得的硬币个数。

思路:

题目其实读起来确实有点绕,但是读懂了就会发现非常简单。因为本题地图只有两行,所以无论Alice在哪个位置 从第一行进入第二行,都会把地图分为两部分(即左下部分和右上部分,这两个部分都是还有硬币的区域),而Bob因为只能向右或向下走,所以他只能选择经过并拾取其中一个部分的硬币,(即选择这两个部分中的最大值)。那么我们只需要枚举Alice的每种走法(即枚举她从哪个位置进入第二行),就可以得到所需的答案了【复杂度是O(n)级别的,n也仅有1e5所以肯定没有问题】。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std; 
#define int long long
#define fi first
#define se second
#define endl '\n'
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
const int N=1e5+5;
int h[N],e[N],nx[N],idx;
int k,T,t,n,m,ans,cnt;
int g[5][N],s1[N],s2[N];
bool vis[N];
priority_queue <int,vector<int>,greater<int> > q;
signed main()
{
        ios::sync_with_stdio(false);
         cin.tie(0),cout.tie(0);
        cin>>T;
        while(T--)
        {
                cin>>n;
                ans=INF;
                for(int i=1;i<=2;i++)
                        for(int j=1;j<=n;j++)
                                cin>>g[i][j];        
                        
                for(int i=1;i<=n;i++)
                        s1[i]=s1[i-1]+g[1][i];
                        
                for(int i=1;i<=n;i++)
                        s2[i]=s2[i-1]+g[2][i];                

                for(int i=1;i<=n;i++)
                {
                        ans=min(ans,max(s1[n]-s1[i],s2[i-1]));        
                }        
                if(ans==INF) cout<<0<<endl;
                else cout<<ans<<endl;                                
        }

        return 0;
}

D、Say No to Palindromes

题意:

给定一个长度为n的字符串,以及m次询问,每次询问给你一个l,r,问你在l~r这个区间中,要让原串这个区间变成不回文所需要更改的字母个数。(注意给定的这个字符串只由a b c三个字母组成,并且更改也只能更改为这三个字母之一)

思路:

我们注意到 原串仅由abc三个字母组成,m,n的范围都为1~2e5,说明暴力的判断肯定会T,这个数据范围肯定是想让我们对于单次查询 应该找到O(n)或O(logn)的做法。我们很容易注意到,若一个字符串中的子串不回文,那它仅有可能是 abcabc、acbacb、bacbac、bcabca、cabcab、cbacba这六种情况,那么我们只需要运用前缀和的思想,记录每种状态的 第一个位置 到 其余每个位置 字符不匹配的数量即可,例如求l~r这个区间,我们可以分别判断六种状态的(pre[r]-pre[l-1]),取六种情况的最小值即是题目所求的答案。

代码:

#include<bits/stdc++.h>
//#pragma GCC optimize(2)
using namespace std; 
#define int long long
#define fi first
#define se second
#define endl '\n'
#define PII pair<int,int>
#define INF 0x3f3f3f3f
#define LINF 0x3f3f3f3f3f3f3f3f
const int N=2e5+5;
int h[N],e[N],nx[N],idx;
int k,T,t,n,m,ans,cnt;
int a[N],pre[10][N];
bool vis[N];
char s[N];
char str[10][10]={" "," abc"," acb"," bac"," bca"," cab"," cba"};
priority_queue <int,vector<int>,greater<int> > q;
void init()
{
        for(int i=1;i<=6;i++)
        {
                for(int x=1,y=1;x<=n;x++,y++)
                {
                        if(y>3) y=1;
                        pre[i][x]=pre[i][x-1]+(s[x]!=str[i][y]);
                }
        }
}
signed main()
{
        ios::sync_with_stdio(false);
         cin.tie(0),cout.tie(0);
        cin>>n>>m>>s+1;
        init();
        while(m--)
        {
                int l,r,res=INF;
                cin>>l>>r;
                for(int i=1;i<=6;i++)
                        res=min(res,pre[i][r]-pre[i][l-1]);
                cout<<res<<endl;                        
        }

        return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值