day2018.5.13模拟赛AK的纪念

这场模拟赛的题目处了T5莫名AC以外,都不是很难.

这场考试六道题,4个小时完成.

T1:

水题一道.

题目大概就是给出T个询问,每个询问输入一个数p,问这个数是不是立方差数.

数据量是T<=100,p<=10^18.

一开始以为要卡long long,卡常数,于是常数好处理啊,long long根本没管它.

那么题目大概就是暴力枚举,但是我用二分AC了.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)      //专属神奇循环简写
#define fuck(mid) mid*mid*mid
int T;
long long p=0;
bool flag=0;
inline void into(){
  scanf("%lld",&p);
  flag=0;
}
inline void work(){
  long long l=1,r=1000001,mid=(l+r)>>1;
  while (l+1<r){
  	if (fuck(mid)==p) {
  	  flag=1;
  	  return;
  	}
    if (fuck(mid)>p) r=mid;
    else l=mid;
    mid=(l+r)>>1;
  }
  if (fuck(l)==p) flag=1;
  else if (fuck(r)==p) flag=1;
}
inline void outo(){
  if (flag) printf("YES\n");
  else printf("NO\n");
}
int main(){
  scanf("%d",&T);
  rep(i,1,T){
    into();
    work();
    outo();
  }
  return 0;
}

第二道题也比较水.

也就是一个数论题.

扫描二维码关注公众号,回复: 859039 查看本文章

这道题的大概意思就是给出T个询问,每个询问一个质数p,问p是不是平方差数.

那么分析一下性质,好像挺简单.

因为p是质数,又要等于a^3-b^3,所以把p因式分解一下就会得出p=(a-b)(a^2+ab+b^2).

因为a>=1,b>=1,所以a-b为1.

所以a=b-1

所以我们只需要枚举a或b就行了.

时间复杂度O(sqrt(p)T).

所以代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)      //专属神奇循环简写
#define fuck(i) i*i*i
long long p;
int T;
bool flag;
inline void into(){
  scanf("%lld",&p);
}
inline void work(){
  flag=0;
  for (int i=1;i<=int(sqrt(p));i++)
    if (fuck((long long)(i+1))-fuck((long long)(i))==p) {
      flag=1;
      return;
    }
}
inline void outo(){
  if (flag) printf("YES\n");
  else printf("NO\n");
}
int main(){
  scanf("%d",&T);
  rep(i,1,T){
    into();
    work();
    outo();
  }
  return 0;
}

T3:

题意大概是给定n,然后对于任意数1<=k<=n,选出几个数,使得这些数两两不等且可以从中选出几个等于k.

然后求最少金币数以及最少金币数的方案总数.

那么第一问很简单,贪心的可以想到1,2,4,8,...肯定最少.

于是第二问我们可以用dp求解.

我们设dp[i][j][k]为数和为i,最大数为j,数的数量为k.

然后得出方程,从f[i][j][k]转移到f[i+1][t][k+t],其中j<t<=k+1.

然后滚动数组优化空间,代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)      //专属神奇循环简写
int n;
int f[1005][1005]={0},dp[1005][1005]={0};
int sum=0,ans=0;
inline void into(){
  scanf("%d",&n);
  sum=int(log(n)/(log(2)))+1;
}
inline void work(){
  dp[1][1]=1;
  rep(i,1,sum-1){
    rep(j,1,n)
      rep(k,1,n)
        if (dp[j][k])
          rep(l,k+1,j+1)
            f[min(n,j+l)][l]+=dp[j][k];
    rep(j,1,n)
      rep(k,1,n)
        dp[j][k]=f[j][k],f[j][k]=0;
  }
}
inline void outo(){
  rep(j,1,n) ans+=dp[n][j];
  printf("%d %d\n",sum,ans);
}
int main(){
  rep(i,1,1){
    into();
    work();
    outo();
  }
  return 0;
}

T4:

题目大意:给出n个数对(ai,bi)和n个数对(xi,yi),若xi>=ai且yi>=bi,则匹配,问最大匹配数.

其中n<=10^5.

正解是贪心+set或线段树.

思路就是按xi和ai从小到大排序,然后扫一遍,将所有aj<xi的(aj,bj)找最大的bj小与yi.

那么思路很清晰,用数组模拟太慢,直接用STL自带的multiset.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)      //专属神奇循环简写
multiset < int > tree;
int n,ans=0;
struct paper{
  int x,y;
}p[100010],q[100010];
bool cmp(paper a,paper b){
  return (a.x<b.x);
}
inline void into(){
  memset(p,0,sizeof(p));
  memset(q,0,sizeof(q));
  scanf("%d",&n);
  rep(i,1,n)
  	scanf("%d%d",&p[i].x,&p[i].y);
  rep(i,1,n)
    scanf("%d%d",&q[i].x,&q[i].y);
  sort(p+1,p+1+n,cmp);
  sort(q+1,q+1+n,cmp);
}
inline void work(){
  int k=1;
  rep(i,1,n){
    while (p[i].x>=q[k].x&&k<=n){
      tree.insert(q[k].y);
      k++;
    }
    if (tree.empty()) continue;
    multiset < int > :: iterator it=tree.upper_bound(p[i].y);
    if (it==tree.begin()) continue;
    ans++;it--;
    tree.erase(it);
  }
}
inline void outo(){
  printf("%d\n",ans);
}
int main(){
  rep(i,1,1){
    into();
    work();
    outo();
  }
  return 0;
}

T5:

这道题最迷了.

题目就是给出n次猜数字li,ri,vi,表示[li,ri]的最小值为vi.

那么求第几次的时候会矛盾.

若n次都不矛盾就输出n+1.

那么这道题明显有一个特性:可二分性.

关于可二分性的讨论本人有另一篇blog写了:https://blog.csdn.net/hzk_cpp/article/details/78463705.

那么二分用什么来判定呢?

我们再来想想样例给我们的信息:

当我们一个大区间的最小值大于了它包含的区间的最小值,那么就不合法.

那么我们可以用数组直接模拟,大概可以O(n^2).

但是我们要更优秀的算法.

我们可以让按照最小值排序,一定让小的在前.

然后再判断后面的区间有没有包含前面的区间.

若有就r=mid,否则l=mid.

那么我们要处理的就是一个问题,如何快速判断区间包含关系.

那么并查集可好用了.

那么就是这样写了:

#include<bits/stdc++.h>
  using namespace std;
int n,T,ans,fa[1000010];
struct node{
  int l,r,x;
}p[1000010],e[1000010];
bool cmp(node a,node b){
  return a.x>b.x;
}
int find(int x){
  if (x==fa[x]) return x;
  return fa[x]=find(fa[x]);
}
bool check(int k){
  for (int i=1;i<=n+1;i++)
    fa[i]=i;
  for (int i=1;i<=k;i++)
    p[i]=e[i];
  sort(p+1,p+1+k,cmp);
  int lmax=p[1].l,lmin=p[1].l,rmax=p[1].r,rmin=p[1].r;
  for (int i=2;i<=k;i++){
    if (p[i].x<p[i-1].x){
      if (find(lmax)>rmin) return true;
      for (int j=find(lmin);j<=rmax;j++)
        fa[find(j)]=find(rmax+1);
      lmin=lmax=p[i].l;
      rmin=rmax=p[i].r;
    }else{
      lmin=min(lmin,p[i].l);
      lmax=max(lmax,p[i].l);
      rmin=min(rmin,p[i].r);
      rmax=max(rmax,p[i].r);
      if (find(lmax)>rmin) return true;
    }
  }
  if (find(lmax)>rmin) return true;
  return false;
}
inline void into(){
  scanf("%d%d",&n,&T);
  for (int i=1;i<=T;i++)
    scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].x);
}
inline void work(){
  int l=1,r=T;
  ans=T+1;
  while (l<=r) {
    int mid=(l+r)>>1;
    if (check(mid)) {
      ans=mid;
      r=mid-1;
    }else l=mid+1;
  }
}
inline void outo(){
  printf("%d\n",ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}

T6:

水题,意思就是给出一个括号序列(只含'('或')'),然后求改变最少次使所有括号得到匹配.

数据保证n<=10^5且是偶数.

那么这道题很简单,大概用一种贪心的思路.

我们把字符串扫一遍,若扫到一个右括号没有匹配,就将它改为左括号.

最后设剩余的左括号数量为x,那么在修改x/2个左括号变为右括号就行了.

所以代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define rep(i,j,k) for (int i=j;i<=k;i++)      //专属神奇循环简写
char c[100005]={0};
int n,sum1=0,sum2=0;
inline void into(){
  scanf("%s",&c);
}
inline void work(){
  n=strlen(c);
  rep(i,0,n-1){
    if (c[i]=='(') sum1++;
    else if (sum1>0) sum1--;
      else sum2++,sum1++;
  }
}
inline void outo(){
  printf("%d\n",sum2+(sum1>>1));
}
int main(){
  rep(i,1,1){
    into();
    work();
    outo();
  }
  return 0;
}
这套试卷AK的很开心.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/80306956