解题报告:Codeforces Round #424 (Div. 2) A B C D E F

A. Unimodal Array 题目链接

题意:

①先严格递增

②相等

③严格递减

只要一个序列的大小变化的顺序满足以上即为 unimodal

现在给定一个序列,询问是否为 unimodal


思路:

分别定义三种情况为0,1,2

然后判断是否出现了倒序即可


代码:

#include<bits/stdc++.h>

using namespace std;


int n;
int A[105];
int main()
{
   scanf("%d",&n);
   for(int i=0;i<n;i++){
      scanf("%d",&A[i]);
   }int k = 0;
   for(int i=1;i<n;i++){
      int t ;
      if(A[i]>A[i-1]){
         t = 0;
      }else if(A[i]==A[i-1]){
         t = 1;
      }else {
         t = 2;
      }if(k>t){
         printf("NO\n");
         return 0;
      }k = t;
   }printf("YES\n");
   return 0;
}


B. Keyboard Layouts 题目链接

题意:

一个键盘上26个字母分别对应了新的26个字母,问现在按下一个对应的字母序列问最后得到的结果

思路:

用map映射一下即可,注意大小写都要映射,不是字母的照常输出

代码:

#include<bits/stdc++.h>

char s1[100];
char s2[100];
char str[1005];
using namespace std;

map<char,char>M;
int main(){
   scanf("%s%s%s",s1,s2,str);
   for(int i=0;i<26;i++){
      M[s1[i]] = s2[i];
      M['A'+s1[i]-'a'] = 'A' + s2[i] - 'a' ;
   }for(int i=0;str[i];i++)
      printf("%c",isalpha(str[i])?M[str[i]]:str[i]);
   return 0;
}


C. Jury Marks 题目链接

这个因为出了一个平时没有注意的BUG,所以单独写了一篇博客

点击打开链接



D. Office Keys 题目链接


题意:

有n个人,k把钥匙,办公室的位置在p,给出每个人的位置,每把钥匙的位置

现在每个人要拿一把钥匙才能进入办公室,每把钥匙只能被一个人用

问所有人都进入办公室的最少要花费的时间


思路:

因为两个人交叉拿钥匙一定不是最优的情况

所以先对每个人的位置进行排序,对每把钥匙的位置进行排序

定义状态dp[ i ][ j ]:前 i 把钥匙让前 j 个人用进入办公室的最少要花费的时间

那么  dp[ i ][ j ] = min( dp[ i-1 ] [ j ] , max( dp[ i-1 ][ j-1 ] , cost ( i , j ) ) )

cost ( i , j ) 为 第 j 个人拿第 i 把钥匙进入办公室花费的时间

复杂度O(n*k + nlog(n) + klog(k) )

代码:

#include<bits/stdc++.h>

using namespace std;

int n,k,p;
int A[1005];
int B[2005];
long long dp[2005][1005];


long long cost(int i,int j){
   return abs(0LL+A[j]-B[i])+abs(0LL+p-B[i]);
}

int main()
{
   memset(dp,0x3f,sizeof(dp));
   scanf("%d%d%d",&n,&k,&p);
   for(int i=1;i<=n;i++){
      scanf("%d",&A[i]);
   }sort(A+1,A+1+n);
   for(int j=1;j<=k;j++){
      scanf("%d",&B[j]);
      dp[j][0] = 0;
   }sort(B+1,B+1+k);
   dp[0][0] = 0;
   for(int i=1;i<=k;i++){
      for(int j=1;j<=min(i,n);j++){
         dp[i][j] = min(dp[i-1][j],max(dp[i-1][j-1],cost(i,j)));
      }
   }printf("%I64d\n",dp[k][n]);
   return 0;
}

E. Cards Sorting 题目链接

题意:

一堆牌以队列的形式放在桌子上,你每次要判断队头是牌是不是最小的牌

如果是,则拿出来,如果不是,则放到队尾

询问把所有牌拿完的操作次数

思路:

很直白的线段树区间查询+单点修改模板题

先用随便一种操作处理出路径,每次查询路径上没有被拿出的牌的数目,然后把路径上最小的牌的位置的权值更新

复杂度O( nlog(n) ) 

代码:

#include<bits/stdc++.h>

#define lson (now<<1)
#define rson lson+1
#define Lson lson,l,(l+r)>>1
#define Rson rson,((l+r)>>1)+1,r
const int MAXN = 1e5+10;
using namespace std;



int n;
int A[MAXN];
int pos[MAXN];

bool cmp(int a,int b){
   if(A[a]!=A[b])return A[a]<A[b];
   return a<b;
}

struct node{
   int val;
}seg[MAXN<<2];

void build(int now,int l,int r){
   if(l==r){
      seg[now].val = 1;
   }else {
      build(Rson);
      build(Lson);
      seg[now].val = seg[lson].val + seg[rson].val ;
   }
}

void update(int now,int l,int r,int pos){
   if(l==r&&l==pos){
      seg[now].val=0;
   }else if(l>pos||r<pos)return ;
   else if(l<=pos&&r>=pos){
      update(Lson,pos);
      update(Rson,pos);
      seg[now].val --;
   }
}

int query(int now,int l,int r,int s,int e){
   if(l>e||r<s)return 0;
   if(l>=s&&r<=e)return seg[now].val;
   return query(Lson,s,e) + query(Rson,s,e);
}

int main()
{
   scanf("%d",&n);
   for(int i=1;i<=n;i++){
      scanf("%d",&A[i]);
      pos[i] = i;
   }sort(pos+1,pos+1+n,cmp);
   long long ans = 0;
   build(1,1,n);
   int index = 0 , now = 1;
   while(index<n){
      int last = index+1 , mi = pos[last] , mx = pos[last];
      while(last<n&&A[pos[last]]==A[pos[last+1]]){
         int p = pos[++last];
         mx = p;
         if(p<now)mi = p;
      }
      if(mi<now){
         ans += query(1,1,n,now,n) + query(1,1,n,1,mi);
         now = mi;
      }else {
         ans += query(1,1,n,now,mx) ;
         now = mx;
      }
      while( ++index <= last )update(1,1,n,pos[index]);
      index--;
   }printf("%I64d\n",ans);
   return 0;
}



F. Bamboo Partition 题目链接

题意:

有n个竹子,竹子每天长1m,每个竹子都有一个你认为的临界高度ai

你每d天去看一次竹子,如果有竹子的高度超过了对应的临界高度,你就会把它砍断(不会再长高),然后把高于ai的部分收集起来

现在求一个最大的d,满足最后收集到的竹子长度小于等于给定的k

思路:

若每d天去看一次竹子,最后收集到的长度为:

要求最大d,使得这个式子最后的值小于等于k,那么整理一下得:

令右式等于sum,两边同时除以d,得:

于是我们发现若两个数d1,d2(d1<=d2)满足

因为d1的左式大于等于d2的左式,所以若d1的不等式为真,那么d2的不等式一定为真

于是我们可以用分块加速的方法check所有块里最大的d,复杂度为O( n * sqrt( k + sum(ai) ) )刚好满足题意

代码:

#include<bits/stdc++.h>

using namespace std;

int n;
int A[105];
long long mx ;
bool check(long long x){
   long long res = 0;
   for(int i=0;i<n;i++){
      long long k = (A[i]-1)/x;
      res += (k*x+x-A[i]);
   }
   return res <= mx;
}

int main()
{
   scanf("%d%I64d",&n,&mx);
   long long s = 1 , e = mx;
   for(int i=0;i<n;i++){
      scanf("%d",&A[i]);
      e += A[i];
   }long long ans = 0;
   for(long long s = 1, last ;s<=e ;s=last+1){
      last = e / ( e / s );
      if(check(last))ans = last;
   }printf("%I64d\n",ans);
   return 0;
}













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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值