【SCOI补全记】SCOI2016 bzoj4567-4572

【SCOI补全记】SCOI2016 bzoj4567-4572T1:背单词(bzoj4567)题解代码T2:幸运数字(bzoj4568)题解代码T3:萌萌哒(bzoj4569)T4:妖怪(bzoj4570)T5:美味(bzoj4571)题解代码T6:围棋(bzoj4572)T1:背单词(bzoj4567)题解关键词:trie树贪心题意有些难懂,简单解释一下。对于在位置xxx的单词:…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺


T1:背单词(bzoj4567)

题解

关键词:trie树 贪心

题意有些难懂,简单解释一下。

对于在位置 x x 的单词:

  • 单词没有后缀,吃
    x

    x
    颗泡椒

    • 所有后缀在 1 1 ~
      x1

      x 1
      中出现,且后缀最靠后位置为 y y ,吃
      xy

      x y
      颗泡椒
    • 有后缀出现在 x x 以后,吃
      n2

      n 2
      颗泡椒
    • 显然最后一种情况是不优的,那么保证所有单词出现在它的后缀以后即可。

      将单词reverse建立一颗“后缀”trie树,即从根出发的任意一条向下路径为某个单词的后缀。按深度遍历保证了不会出现第三种情况。

      剩下的就是贪心了,我们将trie树上代表单词终止点的结点取出来建一棵虚树,把每个点的子节点按子树大小排序,在dfs时先遍历子树size大的显然最优。

      一道看似简单的题,实际上代码还是有点技巧的(时间标记,前驱等等,打WA了几次)。

      代码

      #include<bits/stdc++.h>
      #define RI register
      using namespace std;
      typedef long long ll;
      const int N=510050;
      
      int n,cnt,len,sz[N],bel[N];
      int ch[N][26],ed[N],tim[N],bs;ll ans;
      vector<int>son[N];
      char s[N];
      
      inline bool cmp(const int&x,const int&y){
            
            return sz[x]>sz[y];}
      
      inline void build(int u)
      {
          if(ed[u]) {
              son[bel[u]].push_back(u);
              bel[u]=u;
          }
          for(RI int i=0;i<26;++i) if(ch[u][i]){
              bel[ch[u][i]]=bel[u];
              build(ch[u][i]);
          }
      
      }
      
      inline void getsz(int u)
      {
           sz[u]=ed[u];
           for(RI int i=son[u].size()-1;~i;--i){
              getsz(son[u][i]);
              sz[u]+=sz[son[u][i]];
           }
           sort(son[u].begin(),son[u].end(),cmp);
      }
      
      inline void dfs(int dep,int u,int pre)
      {
          if(ed[u]) {
              tim[u]=++bs;
              ans+= pre? tim[u]-tim[pre]:bs; 
              pre=u;
          }
          for(RI int i=son[u].size()-1;~i;--i)
           dfs(dep+1,son[u][i],pre);
      }
      
      int main(){
          RI int i,j,u,alp;
          scanf("%d",&n);
          for(i=1;i<=n;++i){
              scanf("%s",s+1);
              len=strlen(s+1);
              for(u=0,j=len;j>=1;--j){
                  alp=s[j]-'a';
                  if(!ch[u][alp]) ch[u][alp]=++cnt;
                  u=ch[u][alp];
              }
              ed[u]=1;
          }
          build(0);
          getsz(0);
          dfs(0,0,0);
          printf("%lld\n",ans);
      }

      T2:幸运数字(bzoj4568)

      题解

      关键词:线性基 倍增LCA

      线性基是可以 O(logGi) O ( l o g G i ) 建立和合并的。所以倍增LCA中同样可以存储节点 i i 到第
      2j

      2 j
      次幂个祖先的链上的线性基。

      熟悉线性基的话,很快就能码出来了。时间复杂度 O((n+m)log2Gi) O ( ( n + m ) l o g 2 G i )

      代码

      #include<bits/stdc++.h>
      #define RI register
      #define gc getchar()
      #define si isdigit(c)
      using namespace std;
      typedef long long ll;
      const int N=2e4+100;
      int n,m,F[N][16],d[N];
      int head[N],to[N<<2],nxt[N<<2],tot;
      ll bin[62],ans;
      
      struct P{
        ll bs[62];
      }f[N][16],tp,v[N];
      
      char c;
      template<class T>
      inline void rd(T &x)
      {
          c=gc;x=0;
          for(;!si;c=gc);
          for(;si;c=gc) x=x*10+(c^48);
      }
      
      inline void lk(int u,int v)
      {to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}
      
      inline P merge(P a,P b)
      {
          RI int i,j;ll res;
          for(i=60;~i;--i){
              res=b.bs[i];
              for(j=i;~j && res;--j) if(res&bin[j]){
                  if(!a.bs[j]) a.bs[j]=res;
                  res^=a.bs[j];
              }
           }
          return a;
      }
      
      void dfs(int x)
      {
          RI int i,j;
          for(i=1;bin[i]<=d[x];++i) 
            F[x][i]=F[F[x][i-1]][i-1]; 
          for(i=1;bin[i]<=d[x];++i) 
            f[x][i]=merge(f[x][i-1],f[F[x][i-1]][i-1]);
          for(i=head[x];i;i=nxt[i]){
              j=to[i];if(j==F[x][0]) continue;
              F[j][0]=x;f[j][0]=merge(v[x],v[j]);
              d[j]=d[x]+1;dfs(j);
          }
      }
      
      inline P get(int x,int y)
      {
          tp=v[x];
          if(d[x]<d[y]) swap(x,y);
          RI int i,t=d[x]-d[y];
          for(i=0;bin[i]<=t;++i)
           if(t&bin[i]){
              tp=merge(tp,f[x][i]);
              x=F[x][i];
           }
          if(x==y) return tp;
          for(i=14;~i;--i)
           if(F[x][i]!=F[y][i]){
              tp=merge(tp,f[x][i]);
              tp=merge(tp,f[y][i]);
              x=F[x][i];y=F[y][i];
           }
          tp=merge(tp,merge(f[x][0],f[y][0]));
          return tp;
      }
      
      int main(){
          RI int i,j,k,x,y;ll val;
          bin[0]=1;
          for(i=1;i<=60;++i) bin[i]=bin[i-1]<<1;
          rd(n);rd(m);
          for(i=1;i<=n;++i){
            rd(val);for(j=60;~j;--j) if(val&bin[j]) {v[i].bs[j]=val;break;}
          }
          for(i=1;i<n;++i){rd(x);rd(y);lk(x,y);lk(y,x);}
          dfs(1);
          for(;m;--m){
              ans=0;
              rd(x);rd(y);
              get(x,y);
              for(i=60;~i;--i) 
                ans=max(ans,ans^tp.bs[i]);
              printf("%lld\n",ans);
          }
      }

      T3:萌萌哒(bzoj4569)

      关键词:倍增优化并查集
      传送门


      T4:妖怪(bzoj4570)

      关键词: 凸包
      传送门


      T5:美味(bzoj4571)

      题解

      关键词:主席树 拆位

      因为每次只需要选择最优的一道菜,所以可以拆二进制位对所有菜的评价值建主席树, ai<105 a i < 10 5 ,建立范围为 [0,aimax] [ 0 , a i m a x ] 每次往最优的区间走查询这段区间中是否能取到0/1,从高位到低位逐位选择即可。

      想到拆位就比较好做的一道题。时间复杂度 O(mlog2n) O ( m log 2 ⁡ n )

      代码

      #include<bits/stdc++.h>
      #define RI register
      #define gc getchar()
      #define si isdigit(c)
      #define mid (((l)+(r))>>1)
      using namespace std;
      const int N=2e5+10,mx=(1<<18)-1;
      int now,n,m,qb,qx,ql,qr,des,bin[25];
      int rt[N],ls[N*20],rs[N*20],sz[N*20],tot;
      
      char c;
      inline void rd(int &x)
      {
          c=gc;x=0;
          for(;!si;c=gc);
          for(;si;c=gc) x=x*10+(c^48);
      }
      
      inline void build(int pre,int &nw,int l,int r,int pos)
      {
          nw=++tot;sz[nw]=sz[pre]+1;
          if(l==r) return;
          if(pos<=mid){
             rs[nw]=rs[pre];
             build(ls[pre],ls[nw],l,mid,pos);
          }else{
             ls[nw]=ls[pre];
             build(rs[pre],rs[nw],mid+1,r,pos);
          }
      }
      
      inline bool get(int pre,int nw,int l,int r,int L,int R)
      {
          if(L<=l && r<=R) return (sz[nw]-sz[pre]>0);
          bool re=false;
          if(L<=mid) re=get(ls[pre],ls[nw],l,mid,L,R);
          if(!re && R>mid) re=get(rs[pre],rs[nw],mid+1,r,L,R);
          return re;
      }
      
      int main(){
          RI int i,j,l,r;
          bin[0]=1;for(i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
          rd(n);rd(m);
          for(i=1;i<=n;++i){
             rd(j);
             build(rt[i-1],rt[i],0,mx,j);
          }
          for(;m;--m){
              rd(qb);rd(qx);rd(ql);rd(qr);
              des=mx&(~qb);now=0;
              for(i=17;~i;--i){
                 if(!(qb&bin[i])) now^=bin[i];
                 l=max(now-qx,0);r=(now|(bin[i]-1))-qx;
                 if(r<0 || (!get(rt[ql-1],rt[qr],0,mx,l,r))) now^=bin[i];
              }
              printf("%d\n",now^qb);
          }
      }

      T6:围棋(bzoj4572)

      关键词:轮廓线DP
      传送门


      总结

      考点:
      字符串 × × 1
      倍增 × × 2
      计算几何 × × 1
      线性基/二进制拆位 × × 2
      简单数据结构(简单主席树) × × 1
      DP(轮廓线) × × 1

      倍增/二进制位出现频率较高?
      数据结构较基础?
      DP D P 普遍毒瘤?
      算几年年出现?

      T1,2,5不能错
      T3是一道不可多得的思维好题
      T4算几还需要多加练习
      T6轮廓线DP还不熟练,考试码不出来(似乎是道压轴题,雾)
      思维题偏少

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/159350.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 阿里云分析数据库_阿里云用的什么数据库

    阿里云分析数据库_阿里云用的什么数据库前言由于工作中应用到了阿里的分析型数据库产品,虽然它类似于mysql,但又有一些区别,通过好好的了解它,才能解决自己的性能优化方面的疑惑。一、定义从官方文档了解到其的定义为:阿里云分析型数据库AnalyticDB(简称ADB),是云端托管的PB级高并发实时数据仓库,是专注于服务OLAP领域的数据仓库。在数据存储模型上,采用关系模型进行数据存储,可以使用SQL进行自由灵活的计算分析,无需预…

    2022年9月17日
    0
  • cJSON 使用详解

    cJSON 使用详解由于c语言中,没有直接的字典,字符串数组等数据结构,所以要借助结构体定义,处理json。如果有对应的数据结构就方便一些,如python中用json.loads(json)就把json字符串转变为内建的数据结构处理起来比较方便。一个重要概念:在cjson中,json对象可以是json,可以是字符串,可以是数字。。。cjson数据结构定义:#d…

    2022年6月15日
    69
  • android ListView 嵌套 ListView

    android ListView 嵌套 ListView实现的效果是这个样子的看上去效果还是不错,不过现在有个刷新问题一直没能解决,刷新的时候里面的adapter进行刷新的时候总是会让里面的listview消失掉,应该是父listview先刷新完后,子listview还未刷新完成,导致测量的高度不对,就会消失,像当前组已关闭这种,现在这个问题还没有想到办法解决的,试过比较多的方法,添加接口让子listview刷新完成后再去更新父…

    2022年7月16日
    20
  • 脚本是什么?[通俗易懂]

    脚本是什么?[通俗易懂]初次接触“脚本”一词并不知道这一听似非常高大上的东西是什么,尔后逐渐接触,虽有了解,但也没有仔细地总结和思考过,今日百度了一下,在此小小总结。“脚本”其实就是一段代码,一个程序。这与我们学习C语言时,写的第一个“helloworld”显示程序没有太大的区别,那为什么这个向程序之神打招呼的“helloworld”程序我们不称其为脚本呢?因为“脚本”有这些特别之处:1、脚本语法比较简单…

    2022年10月25日
    0
  • Linux下查看CPU型号,内存大小,硬盘空间的命令(详解)

    Linux下查看CPU型号,内存大小,硬盘空间的命令(详解)

    2021年6月2日
    114
  • ODrive应用 #4 配置参数&指令「建议收藏」

    参数与指令我们将使用作为每个ODrive对象的占位符。每个ODrive控制器都是一个ODrive对象。在odrivetool中通常是odrv0。此外,我们将<axis>用作每个轴的占位符,这是ODrive对象的属性(例如odrv0.axis0)。轴表示电动机的连接位置。(M0和axis0对应,M1和axis1对应)文章目录参数与指令轴相应的指令状态机启动程序控制模式控制指令…

    2022年4月14日
    155

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号