暑假集训CSP提高模拟2

暑假集训CSP提高模拟2

组题人: @Delov

T1 T2745. 活动投票 30pts

  • 原题: luogu P2397 yyy loves Maths VI (mode)

  • 懒得再复制一遍了,直接挂当时的 题解 得了。

    点击查看代码
    int main()
    {
        ll n,a,sum=0,ans=-0x7f7f7f7f,i;
        scanf("%lld",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a);
            if(ans==a)
            {
                sum++;
            }
            else
            {
                sum--;
                if(sum<=0)
                {
                    sum=1;
                    ans=a;
                }
            }
        }
        printf("%lld\n",ans);
        return 0;
    }
    

T2 T2741. 序列 0pts

  • 原题: UVA1608 不无聊的序列 Non-boring sequences
  • 部分分
    • 10pts :莫队处理出所有区间,询问即可。

      点击查看代码
      int a[200010],b[200010],cnt[200010],sum[200010],L[200010],R[200010],pos[200010],klen,ksum;
      struct ask 
      {
          int l,r,id;
      }q[10000010];
      bool q_cmp(ask a,ask b)
      {
          return (pos[a.l]==pos[b.l])?((pos[a.l]%2==1)?(a.r<b.r):(a.r>b.r)):(a.l<b.l);
      }
      void init(int n,int m)
      {
          klen=n/sqrt(m)+1;
          ksum=n/klen;
          for(int i=1;i<=ksum;i++)
          {
              L[i]=R[i-1]+1;
              R[i]=R[i-1]+klen;
          }
          if(R[ksum]<n)
          {
              ksum++;
              L[ksum]=R[ksum-1]+1;
              R[ksum]=n;
          }
          for(int i=1;i<=ksum;i++)
          {
              for(int j=L[i];j<=R[i];j++)
              {
                  pos[j]=i;
              }
          }
      }
      void add(int x)
      {
          cnt[x]++;
          if(cnt[x]==1)
          {
              sum[pos[x]]++;
          }
          if(cnt[x]==2)
          {
              sum[pos[x]]--;
          }
      }
      void del(int x)
      {
          cnt[x]--;
          if(cnt[x]==1)
          {
              sum[pos[x]]++;
          }
          if(cnt[x]==0)
          {
              sum[pos[x]]--;
          }
      }
      int query()
      {
          for(int i=1;i<=ksum;i++)
          {
              if(sum[i]>=1)
              {
                  return 1;
              }
          }
          return 0;
      }
      int main()
      {
          int t,n,m,l,r,flag,i,j;
          scanf("%d",&t);
          for(j=1;j<=t;j++)
          {
              scanf("%d",&n);
              m=flag=0;
              memset(cnt,0,sizeof(cnt));
              memset(sum,0,sizeof(sum));
              for(i=1;i<=n;i++)
              {
                  scanf("%d",&a[i]);
                  b[i]=a[i];
              }
              sort(b+1,b+1+n);
              b[0]=unique(b+1,b+1+n)-(b+1);
              for(i=1;i<=n;i++)
              {
                  a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
              }
              for(l=1;l<=n;l++)
              {
                  for(r=l;r<=n;r++)
                  {
                      m++;
                      q[m].l=l;
                      q[m].r=r;
                      q[m].id=m;
                  }
              }
              init(n,m);
              sort(q+1,q+1+m,q_cmp);
              l=1;
              r=0;
              for(i=1;i<=m;i++)
              {
                  while(l>q[i].l)
                  {
                      l--;
                      add(a[l]);
                  }
                  while(r<q[i].r)
                  {
                      r++;
                      add(a[r]);
                  }
                  while(l<q[i].l)
                  {
                      del(a[l]);
                      l++;
                  }
                  while(r>q[i].r)
                  {
                      del(a[r]);
                      r--;
                  }
                  if(query()==0)
                  {
                      flag=1;
                      break;
                  }
              }
              if(flag==0)
              {
                  printf("non-boring\n");
              }
              else
              {
                  printf("boring\n");
              }
          }
          return 0;
      }
      
  • 正解
    • 线段树优化 DP

      • fl,r 表示 [l,r] 中仅出现 1 次的数的个数。

      • r1r 的过程,实际上是多加了一个 ar 。若 ar 以前没有出现过,则 fk,r=fk,r1+1(k[1,r]) ;否则有 {fk,r=fk1,r(k[1,lastlastar])fk,r=fk,r11(k(lastlastar,lastar])fk,r=fk,r1+1(k(lastar,r]) 。序列合法当且仅当 min1lrn{fl,r}1

      • 区间修改、区间查询的线段树维护即可。

        点击查看代码
        int a[200010],b[200010],f[200010],pos[200010];
        pair<int,int>last[200010];
        struct SMT
        {
            struct SegmentTree
            {
                int l,r,minn,lazy;
            }tree[1600010];
            int lson(int x)
            {
                return x*2;
            }
            int rson(int x)
            {
                return x*2+1;
            }
            void pushup(int rt)
            {
                tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
            }
            void build(int rt,int l,int r,int a[])
            {
                tree[rt].l=l;
                tree[rt].r=r;
                tree[rt].lazy=0;
                if(l==r)
                {
                    tree[rt].minn=a[l];
                    return;
                }
                int mid=(l+r)/2;
                build(lson(rt),l,mid,a);
                build(rson(rt),mid+1,r,a);
                pushup(rt);
            }
            void pushdown(int rt)
            {
                if(tree[rt].lazy!=0)
                {
                    tree[lson(rt)].lazy+=tree[rt].lazy;
                    tree[rson(rt)].lazy+=tree[rt].lazy;
                    tree[lson(rt)].minn+=tree[rt].lazy;
                    tree[rson(rt)].minn+=tree[rt].lazy;
                    tree[rt].lazy=0;
                }
            }
            void update(int rt,int x,int y,int val)
            {
                if(x<=tree[rt].l&&tree[rt].r<=y)
                {
                    tree[rt].lazy+=val;
                    tree[rt].minn+=val;
                    return;
                }
                pushdown(rt);
                int mid=(tree[rt].l+tree[rt].r)/2;
                if(x<=mid)
                {
                    update(lson(rt),x,y,val);
                }
                if(y>mid)
                {
                    update(rson(rt),x,y,val);
                }
                pushup(rt);
            }
            int query(int rt,int x,int y)
            {
                if(x<=tree[rt].l&&tree[rt].r<=y)
                {
                    return tree[rt].minn;
                }
                pushdown(rt);
                int mid=(tree[rt].l+tree[rt].r)/2,ans=0x7f7f7f7f;
                if(x<=mid)
                {
                    ans=min(ans,query(lson(rt),x,y));
                }
                if(y>mid)
                {
                    ans=min(ans,query(rson(rt),x,y));
                }
                return ans;
            }
        }T;
        int main()
        {
            int t,n,flag=0,i,j;
            scanf("%d",&t);
            for(j=1;j<=t;j++)
            {
                scanf("%d",&n);
                flag=0;
                for(i=1;i<=n;i++)
                {
                    scanf("%d",&a[i]);
                    b[i]=a[i];
                    last[i]=make_pair(0,0);
                    pos[i]=0;
                }
                sort(b+1,b+1+n);
                b[0]=unique(b+1,b+1+n)-(b+1);
                for(i=1;i<=n;i++)
                {
                    a[i]=lower_bound(b+1,b+1+b[0],a[i])-b;
                }
                T.build(1,1,n,f);
                for(i=1;i<=n;i++)
                {
                    last[i].first=pos[a[i]];
                    last[i].second=last[last[i].first].first;
                    pos[a[i]]=i;
                    if(last[i].first==0)
                    {
                        T.update(1,1,i,1);
                    }
                    else
                    {
                        T.update(1,last[i].second+1,last[i].first,-1);
                        T.update(1,last[i].first+1,i,1);
                    }
                    if(T.query(1,1,i)<=0)
                    {
                        flag=1;
                        break;
                    }
                }
                if(flag==1)
                {
                    printf("boring\n");
                }
                else
                {
                    printf("non-boring\n");
                }
            }
            return 0;
        }
        
    • 官方题解

      也可以分治递归求解,代码看 luogu 题解吧。

    • 挂一组经典 hack 数据。

      点击查看 hack 数据
      in:
      1
      5
      1 2 1 2 1
      
      ans:
      boring
      
      
      

T3 T6201. Legacy 100pts

  • 原题: CF786B Legacy

  • 部分分

  • 正解

    • 直接挂学习笔记的 题解 了。

      点击查看区间连区间代码
      ll cnt=0;
      struct SMT_Q_BG
      {
          struct SegmentTree
          {
              ll l,r;
          }tree[2000010];
          vector<pair<ll,ll> >e[2000010];
          ll dis[2000010],id[2000010],vis[2000010];
          ll lson(ll x)
          {
              return x*2;
          }
          ll rson(ll x)
          {
              return x*2+1;
          }
          void build(ll rt,ll l,ll r,ll n)
          {
              tree[rt].l=l;
              tree[rt].r=r;
              e[rt+n*4].push_back(make_pair(rt,0));
              if(l==r)
              {
                  id[l]=rt;
                  return;
              }
              e[lson(rt)].push_back(make_pair(rt,0));
              e[rson(rt)].push_back(make_pair(rt,0));
              e[rt+n*4].push_back(make_pair(lson(rt)+n*4,0));
              e[rt+n*4].push_back(make_pair(rson(rt)+n*4,0));
              ll mid=(l+r)/2;
              build(lson(rt),l,mid,n);
              build(rson(rt),mid+1,r,n);
          }
          void update(ll rt,ll x,ll y,ll pd,ll n,ll t,ll w)
          {
              if(x<=tree[rt].l&&tree[rt].r<=y)
              {
                  if(pd==0)
                  {
                      e[rt].push_back(make_pair(n*8+t,0));
                  }
                  else
                  {
                      e[n*8+t].push_back(make_pair(rt+n*4,w));
                  }
                  return;
              }
              ll mid=(tree[rt].l+tree[rt].r)/2;
              if(x<=mid)
              {
                  update(lson(rt),x,y,pd,n,t,w);
              }
              if(y>mid)
              {
                  update(rson(rt),x,y,pd,n,t,w);
              }
          }
          void dijkstra(ll p)
          {
              ll x,i;
              priority_queue<pair<ll,ll> >q;
              memset(dis,0x3f,sizeof(dis));
              memset(vis,0,sizeof(vis));
              dis[p]=0;
              q.push(make_pair(-dis[p],-p));
              while(q.empty()==0)
              {
                  x=-q.top().second;
                  q.pop();
                  if(vis[x]==0)
                  {
                      vis[x]=1;
                      for(i=0;i<e[x].size();i++)
                      {
                          if(dis[e[x][i].first]>dis[x]+e[x][i].second) 
                          {
                              dis[e[x][i].first]=dis[x]+e[x][i].second;
                              q.push(make_pair(-dis[e[x][i].first],-e[x][i].first));
                          }
                      }
                  }
              }
          }
      }T;
      void add(ll a,ll b,ll c,ll d,ll w,ll n)
      {
      	cnt++;
      	T.update(1,a,b,0,n,cnt,w);
          T.update(1,c,d,1,n,cnt,w);
      }
      int main()
      {
          ll n,m,p,pd,w,a,b,c,d,i;
          scanf("%lld%lld%lld",&n,&m,&p);
          T.build(1,1,n,n);
          for(i=1;i<=m;i++)
          {
      		scanf("%lld",&pd);
      		if(pd==1)
      		{
      			scanf("%lld%lld%lld",&a,&c,&w);
      			b=a;
      			d=c;
      		}
      		if(pd==2)
      		{
      			scanf("%lld%lld%lld%lld",&a,&c,&d,&w);
      			b=a;
      		}
      		if(pd==3)
      		{
      			scanf("%lld%lld%lld%lld",&c,&a,&b,&w);
      			d=c;
      		}
              add(a,b,c,d,w,n);
          }
      	T.dijkstra(T.id[p]);
          for(i=1;i<=n;i++)
          {
              if(i==p)
              {
                  printf("0 ");
              }
              else
              {
                  printf("%lld ",(T.dis[T.id[i]+n*4]==0x3f3f3f3f3f3f3f3f)?-1:T.dis[T.id[i]+n*4]);
              }
          }
          return 0;
      }
      

T4 T2731. DP搬运工1 22pts

  • 部分分

    • 22pts :生成所有全排列,依次判断。

      点击查看代码
      const ll p=998244353;
      ll a[100];
      int main()
      {
          ll n,k,sum,ans=0,i;
          cin>>n>>k;
          for(i=1;i<=n;i++)
          {
              a[i]=i;
          }
          do
          {
              sum=0;
              for(i=2;i<=n;i++)
              {
                  sum+=max(a[i],a[i-1]);
              }
              ans=(ans+(sum<=k))%p;
          }while(next_permutation(a+1,a+1+n));
          cout<<ans<<endl;
          return 0;
      }
      
  • 正解

    • 预设性 DP 主要思路是枚举填哪个数。
    • 钦定从小到大填数,设 fi,j,k 表示填到 i 时, [1,n] 中共有 j+1 个连续段已经填数了,相邻两数最大值之和为 k 的方案数。
    • 考虑刷表。
      • 若新填入的数在两端
        • 若没有相邻元素,填入的数则不会对答案产生贡献,有 fi,j+1,k+=2fi1,j,k
        • 若有相邻元素,填入的数则会对答案产生贡献,有 fi,j,k+i+=2fi1,j,k
      • 若新填入的数在中间
        • 若没有相邻元素,有 j 个位置可以填数(可以把序列分开,硬插进去),有 fi,j+1,k+=j×fi1,j,k
        • 若有一个相邻元素,有 j 个位置可以填数,有 fi,j,k+i+=2j×fi1,j,k
        • 若有两个相邻元素,有 j 个位置可以填数,有 fi,j1,k+2i+=j×fi1,j,k
    • 边界为 f1,0,0=1
    • 最终,有 i=0mfn,0,i 即为所求。
    点击查看代码
    const ll p=998244353;
    ll f[60][60][2510];
    int main()
    {
        ll n,m,ans=0,i,j,k;
        cin>>n>>m;
        f[1][0][0]=1;
        for(i=2;i<=n;i++)
        {
            for(j=0;j<=n-i+1;j++)
            {
                for(k=0;k<=m;k++)
                {
                    f[i][j+1][k]=(f[i][j+1][k]+2*f[i-1][j][k]%p)%p;
                    f[i][j][k+i]=(f[i][j][k+i]+2*f[i-1][j][k])%p;
                    f[i][j+1][k]=(f[i][j+1][k]+j*f[i-1][j][k]%p)%p;
                    f[i][j][k+i]=(f[i][j][k+i]+2*j*f[i-1][j][k]%p)%p;
                    if(j-1>=0)
                    {
                        f[i][j-1][k+2*i]=(f[i][j-1][k+2*i]+j*f[i-1][j][k]%p)%p;
                    }
                }
            }
        }
        for(i=0;i<=m;i++)
        {
            ans=(ans+f[n][0][i])%p;
        }
        cout<<ans<<endl;
        return 0;
    }
    

总结

  • T1 看到是原题后,马上就写了,结果把将 sum=1 写成 sum++; 了,迷之自信让我没再去测别的样例,挂了 70pts
  • T2 两次大样例有点弱,都把乱搞做法放过去了。
  • T3 线段树优化建图贺的 luogu P6348 [PA2011] Journeys 代码;边权的处理想了想就会了;当时写的是 01BFS ,赛时在想 Dijkstra 怎么写,真就应了前几天听牛客的课时授课老师说的“要把广搜,Dijkstra,SPFA,堆优化 Prim 这几个容易搞混的代码之间的不同点搞清楚,别整的最后场上一直在纠结某个地方应该怎么写”。

后记

  • 压缩包头一次见有密码,密码是 mimashimima
  • @xrlong 瑞平“不出线性 DP 的模拟赛不是好模拟赛,不出区间 DP 的模拟赛不是好模拟赛” ,被 @Delov 学长听见了。
  • T2 @Delov 学长实行赛时翻提交代码,然后加 hack 政策。
  • @Delov 学长称整场都是让见见题的,一些套路如果之前不知道的话就感觉不太可做,符合昨天所说的今天这场模拟赛会很难。
  • 貌似 @Delov 学长在比赛开始 1h 左右,就开始看榜上前几及各题首切的人的 luogu 主页、各原题提交记录、博客园和少量博客,目测看了 @xrlong@wkh2008 和我的。炸裂的是看 @xrlong2024.7.5 鲜花 将我给的图点开后,找到我博客时随机出来的也是这张图;看我博客的时候看着他对着摘要愣了会儿,不知道在干啥。
posted @   hzoi_Shadow  阅读(68)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2023-07-19 [人生哲理]吕氏春秋 续集
扩大
缩小
点击右上角即可分享
微信分享提示