2018.12.4 队测总结+题解

本文深入探讨了竞赛编程中常见的算法挑战,包括动态规划、数据结构优化及线段树应用,通过具体题目解析,如合并奶牛问题和牛奶装瓶问题,展示了高效算法设计与实现的全过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

吐槽

90+0+90

这套题……数据好弱,我两个错误算法出题人居然每个只卡了10pts……

题解

T1 合并奶牛

官方题解没看懂……自己瞎搞出了一个做法。

f[i][j]f[i][j]f[i][j]表示第一个序列取前iii个,第二个序列取前jjj个构成的序列的方案数。

假设所有数互不相同,那么显然
f[i][j]=f[i−1][j]+f[i][j−1] f[i][j]=f[i-1][j]+f[i][j-1] f[i][j]=f[i1][j]+f[i][j1]
考虑如何去重。先放式子
f[i][j]=f[i−1][j]+f[i][j−1]−∑a[i−k+1,i]=b[j−k+1,j]f[i−k][j−k]×cat[k−1] f[i][j]=f[i-1][j]+f[i][j-1]-\sum_{a[i-k+1,i]=b[j-k+1,j]} f[i-k][j-k]\times cat[k-1] f[i][j]=f[i1][j]+f[i][j1]a[ik+1,i]=b[jk+1,j]f[ik][jk]×cat[k1]
其中catcatcat表示卡特兰数,第000项到第444项分别为1,1,2,5,141,1,2,5,141,1,2,5,14

如何证明?

显然对于两个序列都相同的情况,可以钦定第一个取走的个数一定大于第二个,可以转化成括号序列问题,那么答案就是cat[n]cat[n]cat[n]

类似上面的情况,对于两个相等的部分,假设长度最长的公共部分长度为LLL,那么要去重就是这长度LLL用括号序列来保证没有重复。

那么上面减去的那个∑\sum后面的部分就是枚举括号序列的断点,使得后面的括号序列不可能产生合法的端点,假设为kkk,那么后面部分的方案数就是cat[k−1]cat[k-1]cat[k1],而前面部分的方案数就是f[i−k][j−k]f[i-k][j-k]f[ik][jk],由于前面加的部分的特性使得不合法的方案必定等于合法的方案,因此上面式子是对的。

时间复杂度看起来像O(n3)O(n^3)O(n3),但是实际上是O(n2)O(n^2)O(n2)的。(然而这个写法在时间空间代码量三个方面踩了标算)

T2 寻找规律

不会,会了再来填坑。

T3 牛奶装瓶

一种奇怪的线段树,整除直接除,有一个优化就是当一段区间的⌊maxv⌋=⌊minv⌋\lfloor\frac{max}{v}\rfloor =\lfloor\frac{min}{v}\rfloorvmax=vmin时直接变成区间加法。

底下的代码有一个非常奇怪的cov标记,不要管,那是我90pts代码的一部分……

代码

T1 合并奶牛

#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=2000;
const int mod=998244353;

int n,a[maxn+10],b[maxn+10],f[maxn+10][maxn+10],cat[maxn+10];

int main()
{
  freopen("merge.in","r",stdin);
  freopen("merge.out","w",stdout);
  n=read();
  for(int i=1; i<=n; ++i)
    {
      a[i]=read();
    }
  for(int i=1; i<=n; ++i)
    {
      b[i]=read();
    }
  cat[0]=1;
  for(int i=1; i<=n; ++i)
    {
      for(int j=0; j<i; ++j)
        {
          cat[i]=(cat[i]+1ll*cat[j]*cat[i-1-j])%mod;
        }
    }
  for(int i=0; i<=n; ++i)
    {
      f[i][0]=f[0][i]=1;
    }
  for(int i=1; i<=n; ++i)
    {
      for(int j=1; j<=n; ++j)
        {
          f[i][j]=f[i-1][j]+f[i][j-1];
          if(f[i][j]>=mod)
            {
              f[i][j]-=mod;
            }
          for(int k=1; k<=std::min(i,j); ++k)
            {
              if(a[i-k+1]!=b[j-k+1])
                {
                  break;
                }
              f[i][j]=(f[i][j]-1ll*cat[k-1]*f[i-k][j-k])%mod;
              if(f[i][j]<0)
                {
                  f[i][j]+=mod;
                }
            }
        }
    }
  printf("%d\n",f[n][n]);
  return 0;
}

T3 牛奶装瓶

#include <cstdio>
#include <algorithm>

int read()
{
  int x=0,f=1;
  char ch=getchar();
  while((ch<'0')||(ch>'9'))
    {
      if(ch=='-')
        {
          f=-f;
        }
      ch=getchar();
    }
  while((ch>='0')&&(ch<='9'))
    {
      x=x*10+ch-'0';
      ch=getchar();
    }
  return x*f;
}

const int maxn=100000;
const int inf=0x7fffffff;

int n,q,st[maxn+10];

int divide(int x,int y)
{
  if(x<0)
    {
      return (x-y+1)/y;
    }
  return x/y;
}

namespace sgt
{
  long long sum[maxn<<2];
  int icov[maxn<<2],cov[maxn<<2],mn[maxn<<2],mx[maxn<<2],adt[maxn<<2];
  int tl[maxn<<2],tr[maxn<<2];

  int putadt(int x,int l,int r,int v)
  {
    sum[x]+=1ll*(r-l+1)*v;
    if(icov[x])
      {
        cov[x]+=v;
      }
    else
      {
        adt[x]+=v;
      }
    mn[x]+=v;
    mx[x]+=v;
    return 0;
  }

  int putcov(int x,int l,int r,int v)
  {
    adt[x]=0;
    icov[x]=1;
    cov[x]=v;
    sum[x]=1ll*(r-l+1)*v;
    mn[x]=mx[x]=v;
    return 0;
  }

  int pushdown(int x,int l,int r)
  {
    int mid=(l+r)>>1;
    if(icov[x])
      {
        putcov(x<<1,l,mid,cov[x]);
        putcov(x<<1|1,mid+1,r,cov[x]);
      }
    else
      {
        putadt(x<<1,l,mid,adt[x]);
        putadt(x<<1|1,mid+1,r,adt[x]);
        adt[x]=0;
      }
    return 0;
  }

  int updata(int x)
  {
    sum[x]=sum[x<<1]+sum[x<<1|1];
    mn[x]=std::min(mn[x<<1],mn[x<<1|1]);
    mx[x]=std::max(mx[x<<1],mx[x<<1|1]);
    if(mn[x]==mx[x])
      {
        icov[x]=1;
        cov[x]=mn[x];
      }
    else
      {
        icov[x]=0;
      }
    return 0;
  }

  int build(int x,int l,int r)
  {
    if(l==r)
      {
        putcov(x,l,r,st[l]);
        return 0;
      }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    tl[x]=l;
    tr[x]=r;
    updata(x);
    return 0;
  }

  int getmin(int x,int l,int r,int askl,int askr)
  {
    if((askl<=l)&&(r<=askr))
      {
        return mn[x];
      }
    pushdown(x,l,r);
    int mid=(l+r)>>1,res=inf;
    if(askl<=mid)
      {
        res=std::min(res,getmin(x<<1,l,mid,askl,askr));
      }
    if(mid<askr)
      {
        res=std::min(res,getmin(x<<1|1,mid+1,r,askl,askr));
      }
    return res;
  }

  long long getsum(int x,int l,int r,int askl,int askr)
  {
    if((askl<=l)&&(r<=askr))
      {
        return sum[x];
      }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    long long res=0;
    if(askl<=mid)
      {
        res+=getsum(x<<1,l,mid,askl,askr);
      }
    if(mid<askr)
      {
        res+=getsum(x<<1|1,mid+1,r,askl,askr);
      }
    return res;
  }

  int add(int x,int l,int r,int askl,int askr,int v)
  {
    if((askl<=l)&&(r<=askr))
      {
        putadt(x,l,r,v);
        return 0;
      }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    if(askl<=mid)
      {
        add(x<<1,l,mid,askl,askr,v);
      }
    if(mid<askr)
      {
        add(x<<1|1,mid+1,r,askl,askr,v);
      }
    updata(x);
    return 0;
  }

  int div(int x,int l,int r,int askl,int askr,int v)
  {
    if((mn[x]>=-1)&&(mx[x]<=0))
      {
        return 0;
      }
    if((askl<=l)&&(r<=askr)&&(icov[x]))
      {
        putcov(x,l,r,divide(cov[x],v));
        return 0;
      }
    if((askl<=l)&&(r<=askr)&&(divide(mx[x],v)-mx[x]==divide(mn[x],v)-mn[x]))
      {
        putadt(x,l,r,divide(mx[x],v)-mx[x]);
        return 0;
      }
    pushdown(x,l,r);
    int mid=(l+r)>>1;
    if(askl<=mid)
      {
        div(x<<1,l,mid,askl,askr,v);
      }
    if(mid<askr)
      {
        div(x<<1|1,mid+1,r,askl,askr,v);
      }
    updata(x);
    return 0;
  }
}

int main()
{
  freopen("milk.in","r",stdin);
  freopen("milk.out","w",stdout);
  n=read();
  q=read();
  for(int i=1; i<=n; ++i)
    {
      st[i]=read();
    }
  sgt::build(1,1,n);
  while(q--)
    {
      int op=read(),a=read(),b=read();
      if(op==1)
        {
          int c=read();
          sgt::add(1,1,n,a,b,c);
        }
      else if(op==2)
        {
          int c=read();
          sgt::div(1,1,n,a,b,c);
        }
      else if(op==3)
        {
          printf("%d\n",sgt::getmin(1,1,n,a,b));
        }
      else
        {
          printf("%lld\n",sgt::getsum(1,1,n,a,b));
        }
    }
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值