day2018.6.27模拟赛总结

今天总是看错题怎么办???


T1:

题目大意:维护一个序列,每个元素是一个栈,支持以下操作:

1.格式a v,表示将v复制一份并往栈顶插入一个元素i后放入元素i中.

2.格式b v,表示将v复制一份,查询并删除栈顶后放入元素i中.

3.格式c v w,表示将v复制一份放入元素i中,并查询元素w和v中有多少个相同的元素.

注:1<=序列元素个数n<=300000.

考场想了好久,然后突然发现自己将题目理解错了...

然后理解正确之后想了一会就得出正解...

就是构造一颗树,每个树都存一个栈,其中每个节点k的父亲是k去掉栈顶元素.

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

那么我们就可以以空栈为根节点,建立一颗树型结构,维护可持久化栈.

然后两个节点的相同元素数量就是他们的lca的元素数量了.

考场AC如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
char rc(){
  char c=getchar();
  for (;c<'a'||c>'c';c=getchar());
  return c;
}
int ri(){
  int x=0;
  char c=getchar();
  for (;c<'0'||c>'9';c=getchar());
  for (;c<='9'&&c>='0';c=getchar()) x=x*10+c-'0';
  return x;
}
const int N=300000;
const int M=19;
struct Node{
  int top,num,grand[M+1];
}nod[N+5];
struct Change{
  int num,v,w,ans;
}c[N+5];
int n,a[N+5],top;
ACF into(){
  char cc;
  int w,v;
  n=ri();
  nod[++top].num=1;a[0]=1;
  for (int i=1;i<=n;i++){
    cc=rc();
    switch (cc){
      case 'a':v=ri();
               c[i].num=1;
  	       c[i].v=v;
  	       a[i]=++top;
               nod[top].top=i;
               nod[top].num=nod[a[v]].num+1;
               nod[top].grand[0]=a[v];
               for (int i=1;i<=M;i++)
                 nod[top].grand[i]=nod[nod[top].grand[i-1]].grand[i-1];
               break;
      case 'b':v=ri();
               if (a[v]==1) {
                 a[i]=1;
                 c[i].num=2;
                 c[i].v=v;
                 c[i].ans=0;
               }else{
                 c[i].num=2;
                 c[i].v=v;
                 c[i].ans=nod[a[v]].top;
                 a[i]=nod[a[v]].grand[0]; 
               }
               break;
      case 'c':v=ri(),w=ri();
               c[i].num=3;
               c[i].v=v,c[i].w=w;
               a[i]=a[v];
               break;
    }
  }
}
int lca(int u,int v){
  if (nod[u].num<nod[v].num) swap(u,v);
  for (int i=M;i>=0;i--)
    if (nod[nod[u].grand[i]].num>=nod[v].num) u=nod[u].grand[i];
  for (int i=M;i>=0;i--)
    if (nod[u].grand[i]!=nod[v].grand[i]) u=nod[u].grand[i],v=nod[v].grand[i];
  if (u==v) return nod[u].num-1;
  else return nod[nod[u].grand[0]].num-1;
}
ACF work(){
  for (int i=1;i<=n;i++)
    if (c[i].num==3) c[i].ans=lca(a[c[i].v],a[c[i].w]);
}
ACF outo(){
  for (int i=1;i<=n;i++)
    if (c[i].num!=1) printf("%d\n",c[i].ans);
}
int main(){
  freopen("stogovi.in","r",stdin);
  freopen("stogovi.out","w",stdout);
  into();
  work();
  outo();
  return 0;
} 


T2:

考场上什么都没想只想写暴力...

题目大意:

S(1)=B.

S(n)=S(n-1)+B+reverse(flip(S(n-1))).

其中reverse表示翻转字符串,flip表示将字符串中的B改成D,D改成B.

现在回答T个询问,每个询问[l,r]表示询问S(2^1000)中区间[l,r]中的B的数量.

注:1<=T<=1000    1<=L<=R<=10^18.

考场L<=R<=10^6的30分暴力分拿到了...

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
const int N=1000000;
int a[N*2+10],n,l,r;
bool b[N*2+10];
ACF start(){
  b[1]=1;n=1;
  for (;n<=1000000;){
    b[n+1]='B';
    for (int i=1;i<=n;i++)
      b[n+i+1]=!b[n-i+1];
    n<<=1;n++;
  }
  for (int i=1;i<=n;i++)
    a[i]=a[i-1]+(b[i]?1:0);
}
ACF into(){
  scanf("%d%d",&l,&r);
}
ACF work(){
}
ACF outo(){
  printf("%d\n",a[r]-a[l-1]);
}
int main(){
  freopen("bd.in","r",stdin);
  freopen("bd.out","w",stdout);
  int T;
  start();
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
}

然后这道题其实正解也不难,因为到其实S(n)和S(n-1)的前半部分其实是一样的,所以其实我们只需要处理到S(62)就行了.

那么暴力处理出S(1~68)的B的数量.

然后我们考虑将查询[l,r]拆成查询[1,r]-[1,l-1].

然后用f(n)表示查询[1,n]的答案.

那么很明显的是我们刻意把一段区间[1,n]的答案这样拆分:


那么剩余部分的答案肯定是已处理出来的部分的一部分,这一部分又可以通过已预处理的部分-f(去掉剩余部分的前面一段)求出.

用图可以这样理解:


然后就可以证明时间复杂度是O(log(n))的.

AC代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define ACF inline void
typedef long long LL;
LL S[63],l,r,le[63]; 
int n;
ACF start(){
  int r=1;
  S[1]=1LL;le[1]=1LL;
  for (int i=2;i<=62;i++)
    le[i]=le[i-1]*2+1,S[i]=1+le[i-1];      //S[i]=S[i-1]+1+le[i-1]-S[i-1]=1+le[i-1] 
}
ACF into(){
  scanf("%lld%lld",&l,&r);
}
ACF work(){
}
LL f(LL R,int k=62){
  if (!R) return 0;
  if (le[k]>=R) return f(R,k-1);
  else return f(R-le[k]-1,k-1)+S[k]+1-(R>le[k]+1+le[k]/2);
}
ACF outo(){
  printf("%lld\n",f(r)-f(l-1));
}
int main(){
  freopen("bd.in","r",stdin);
  freopen("bd.out","w",stdout);
  int T;
  start();
  scanf("%d",&T);
  while (T--){
    into();
    work();
    outo();
  }
  return 0;
} 


T3:

题目大意:给出一条数轴,每个整数点上有一座城市,有N辆卡车,每辆卡车有一套序列k来叙述,表示卡车的初始点、转折点和终止点.现在有m个询问,每个询问两个数,表示查询这两个数迎面相撞次数,注意,当有一辆车掉头获正出发或结束时不算.

注:n<=10^5    1<=k的和<=2*10^5.

这道题一看就不是代码很短的题,在考试的时候没写...

正解就是考虑暴力,对于每两辆卡车i,j,我们在O(Ki+Kj)的时间内回答询问.

那么我们把这两辆车的所有转折点搞下来,然后维护两个指针i,j,每次转移的时候i和j都一个偏移到转折点或两个都偏移到转折点,就可以每一段都快速判断,然后开心地AC这道题了.

AC代码今天没码好,下次有时间了补.

猜你喜欢

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