吐槽

90+0+90

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

题解

T1 合并奶牛

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

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

假设所有数互不相同,那么显然
f[i][j]=f[i1][j]+f[i][j1] f[i][j]=f[i-1][j]+f[i][j-1]
考虑如何去重。先放式子
f[i][j]=f[i1][j]+f[i][j1]a[ik+1,i]=b[jk+1,j]f[ik][jk]×cat[k1] 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]
其中catcat表示卡特兰数,第00项到第44项分别为1,1,2,5,141,1,2,5,14

如何证明?

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

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

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

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

T2 寻找规律

不会,会了再来填坑。

T3 牛奶装瓶

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

底下的代码有一个非常奇怪的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;
}