2024初三年后集训模拟测试3

前言

image

难度不好说,感觉是东拼西凑的题,但是除了 \(T1\) 都相当不简单。

而且不知道为啥以后的比赛时间都少了半个小时。

  • \(T1~100pts:\)

    签到题,贪心即可。

  • \(T2~70pts:\)

    读假题了,是最大 \(w_i\) 不是固定 \(w_i\) ,做法是 二分答案+DP ,不过需要单调队列优化,不会这玩意儿赛后学了好久 \(qwq\)

    但是读假题了还能拿 \(70pts\)

  • \(T3~0pts:\)

    不会,想到了树形 \(DP\) ,但建树都没建明白。

    题解给了一半,正解没给,给了个复杂度 \(O(n^2)\) 的要过 \(3e5\)差评!

  • \(T4~0pts:\)

    本来人手骗 \(20pts\) 的,\(continue\) 加错地方了,喜提 \(0pts\)

    题解更加抽象,教练发现快吃晚饭了还没有人 \(A\) ,直接又把代码放里面了;至今 2024年02月21日20:55:27 还是没人 \(A\) ,又搞来一份详细题解,把代码加上了注释。

    可怜其良苦用心,但那篇题解码风太抽象了……

T1 排序

  • 题意:

    给定 \(4n\) 个数 \(s_i\) ,分别 \(n\) 组,对于每组 \(4\) 个数 \(a,b,c,d\) ,求所有组 \(|ab-cd|\) 的最大和。

  • 解法:

    • 导入一个常识:

      已知 \(s\) ,将其拆分为 \(s=a+b\) ,若想 \(a\times b\) 尽可能小,则 \(|a-b|\) 尽可能大;若想 \(a\times b\) 尽可能大,则 \(|a-b|\) 尽可能小。

      和相同周长的 \(S_{正方形}>S_{长方形}\) 是一个道理。

    先将所有 \(s_i\) 排序,设 \(ab>cd\)

    将所有 \(s_i\) 分为大的和小的两组,\(a,b\) 相邻搭配,\(c,d\) 分别从两边取即可。

    直接看代码就行了,签到题。

点击查看代码
#include<bits/stdc++.h>
#define int long long 
#define endl '\n'
using namespace std;
const int N=4e5+10;
template<typename Tp> inline void read(Tp&x)
{
    x=0;register bool z=1;
    register char c=getchar();
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
    for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    x=(z?x:~x+1);
}
int n,a[N],ans,sum;
signed main()
{
	#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    read(n);
    for(int i=1;i<=4*n;i++)
        read(a[i]);
    stable_sort(a+1,a+1+4*n);
    for(int i=1,j=4*n;i<=n,j>=2*n+1;i++,j-=2)
        ans+=a[j]*a[j-1]-a[i]*a[2*n-i+1];
    cout<<ans;
}

T2 牛吃草

  • 题意:

image

此处注意他是延伸距离最大为 \(w_i\) ,不是固定 \(w_i\) ,赛时因为这个读假题了 \(qwq\)

  • 部分分:

    • \(70pts:\)

      读假题的情况,二份答案+ \(O(n)的DP\)

      定义 \(f[i][1]\) 为第 \(i\) 个位置上放的最大覆盖长度,反之 \(f[i][0]\) 就是不放的情况。

      转移方程为:

      \(f[i][1]=\max(f[i-len[i]][1],f[i-len[i]][0])+len[i];\)

      \(f[i][0]=\max(f[i-1][1],f[i-1][0])\)

      此处 \(len_i\) 为其真实的 \(w_i\) ,因为他左端点顶到头就 \(1\) 了,再往前就负了,比如不管 \(w_1\) 为多少,\(len_1\)\(=1\)

      因为读假题了, \(DP\) 自然成 \(O(n)\) 了。

      复杂度最坏情况下 \(O(n\log(n))\) ,实则只要 \(f_i>=s\) 就说明满足,跳出就行了,所以真实复杂度远小于此。

      点击查看代码
      #include<bits/stdc++.h>
      #define int long long 
      #define endl '\n'
      using namespace std;
      const int N=5e5+10;
      template<typename Tp> inline void read(Tp&x)
      {
          x=0;register bool z=1;
          register char c=getchar();
          for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
          for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
          x=(z?x:~x+1);
      }
      int n,s,l,r,maxx,mid,ans,w,f[N][2];
      struct aa
      {
          int l,len;
      }a[N];
      bool DP(int x,int s)
      {
          for(int i=1;i<=n;i++)
              f[i][0]=f[i][1]=0;
          for(int i=1;i<=n;i++)
          {
              if(a[i].len>=x) 
                  f[i][1]=max(f[a[i].l-1][1],f[a[i].l-1][0])+a[i].len;
              f[i][0]=max(f[i-1][1],f[i-1][0]);
              if(f[i][1]>=s||f[i][0]>=s) return 1;
          }
          return 0;
      }
      signed main()
      {
          #ifndef ONLINE_JUDGE
          freopen("in.txt","r",stdin);
          freopen("out.txt","w",stdout);
          #endif
          read(n);
          for(int i=1;i<=n;i++) 
              read(w),
              a[i].l=max(1ll,i-w+1),
              a[i].len=i-a[i].l+1,
              maxx=max(maxx,a[i].len);
          read(s);
          s=ceil(double(n*s/100.0));
          l=1,r=maxx;
          while(l<=r)
          {
              mid=(l+r)>>1;
              if(DP(mid,s)) ans=max(ans,mid),l=mid+1;
              else r=mid-1;
          }
          cout<<ans;
      }
      
    • \(90pts:\)

      把题读真了,发现还需要套一层循环。

      循环 \(j=\{i-len_i\sim i-size\}\) ,那么转移方程为:

      \(f[i][1]=\max(\{f[i][1],f[j][1]+i-j,f[j][0]+i-j\});\)

      \(f[i][0]\)\(70pts\) 的一样。

      这样复杂度最坏就成了 \(O(n^2\log(n))\) ,虽然真实情况比这小,但还是会 \(TLE\) 两个点。

      点击查看代码
      #include<bits/stdc++.h>
      #define int long long 
      #define endl '\n'
      using namespace std;
      const int N=5e5+10;
      template<typename Tp> inline void read(Tp&x)
      {
          x=0;register bool z=1;
          register char c=getchar();
          for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
          for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
          x=(z?x:~x+1);
      }
      int n,s,l,r,maxx,mid,ans,w,f[N][2];
      struct aa
      {
          int l,len;
      }a[N];
      bool DP(int x,int s)
      {
          f[0][0]=f[0][1]=0;
          for(int i=1;i<=n;i++)
          {
              f[i][1]=0;
              for(int j=i-a[i].len;j<=i-x;j++)
                  f[i][1]=max({f[i][1],f[j][1]+i-j,f[j][0]+i-j});
              f[i][0]=max(f[i-1][1],f[i-1][0]);
              if(f[i][1]>=s||f[i][0]>=s) return 1;
          }
          return 0;
      }
      signed main()
      {
          #ifndef ONLINE_JUDGE
          freopen("in.txt","r",stdin);
          freopen("out.txt","w",stdout);
          #endif
          read(n);
          for(int i=1;i<=n;i++) 
              read(w),
              a[i].l=max(1ll,i-w+1),
              a[i].len=i-a[i].l+1,
              maxx=max(maxx,a[i].len);
          read(s);
          s=ceil(double(n*s/100.0));
          l=1,r=maxx;
          while(l<=r)
          {
              mid=(l+r)>>1;
              if(DP(mid,s)) ans=max(ans,mid),l=mid+1;
              else r=mid-1;
          }
          cout<<ans;
      }
      
  • 正解:

    \(90pts\) 的做法上使用单调队列优化 \(DP\)

    ∵ 题目数据范围中给了 \(w_{i-1}\geq w_i-1\)

    \(i-1-w_{i-1}\leq i-1-(w_i-1)\)

    \(i-1-w_{i-1}\leq i-w_i\)

    由此,可以使用单调队列优化 \(DP\)

    至于这玩意之前给跳了 \(qwq\)反正会的肯定会的

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    using namespace std;
    const int N=5e5+10;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    int n,s,l,r,maxx,mid,ans,w,f[N][2],t,head,tail,len[N];
    struct aa
    {
        int id,num;
    }q[N];
    bool DP(int x,int s)
    {
        head=1,tail=0;
        f[0][1]=f[0][0]=0;
        for(int i=1;i<=n;i++)
        {
            f[i][0]=f[i][1]=0;
            if(i>=x)
            {
                t=max(f[i-x][1],f[i-x][0])+n-(i-x);
                while(head<=tail&&q[tail].num<t) tail--;
                q[++tail].num=t;
                q[tail].id=i-x;
                while(head<=tail&&q[head].id<i-len[i]) head++;
            }
            f[i][0]=max(f[i-1][1],f[i-1][0]);
            f[i][1]=max(f[i][1],(head>tail?0:q[head].num)+i-n);
            if(f[i][1]>=s||f[i][0]>=s) return 1;
        }
        return 0;
    }
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        read(n);
        for(int i=1;i<=n;i++) 
            read(w),
            l=max(1ll,i-w+1),
            len[i]=i-l+1,
            maxx=max(maxx,len[i]);
        read(s);
        s=ceil(double(n*s/100.0));
        l=1,r=maxx;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(DP(mid,s)) ans=max(ans,mid),l=mid+1;
            else r=mid-1;
        }
        cout<<ans;
    }
    

T3 树上的宝藏

  • 题意:

    对于一颗数上有 \(n\) 个节点,每个节点上填入 \(1\)\(0\)\(n-1\) 条边。

    每个普通的边连接的两点最多只能有一个 \(1\) ,也就是 \([1,0],[0,1],[0,0]\) 三种情况。

    每个特殊的边连接的两点最少必须有一个 \(1\) ,也就是 \([1,0],[0,1],[1,1]\) 三种情况。

    求在第 \(i\) 条边为特殊边,其余为普通边的情况下可行的情况数量。

  • 部分分:

    • \(30pts:\) 纯暴力。

    • \(60pts:\)

      树形 \(DP\) 显然,下面说思路:

      对于第 \(i\) 条边,将其两节点 \(a,b\) 分别为根拆成两棵树,在每棵树上跑树形 \(DP\)

      定义 \(f[i][1]\) 为以 \(i\) 为根的数(子树)\(i\) 处为 \(1\) 的情况数,同样的 \(f[i][0]\) 就是 \(i\) 处为 \(0\) 的情况数。

      结合对于题意的分析,若根节点为 \(1\) ,则子节点必须为 \(0\) ;若根节点为 \(0\) ,则子节点可以为 \(1\)\(0\)

      那么便有了转移方程,设 \(u,v\) 分别为根节点和叶节点:

      \(f[u][1]\times =f[v][0]\)

      \(f[u][0]\times =(f[v][1]+f[v][0])\)

      而问题来到怎么打又儿子向父亲传的树形 \(DP\)

      先跑一遍 \(dfs\) ,处理出每个节点的 \(son\)\(father\) ,然后根据此来跑树形 \(DP\) 就可以了,具体的看代码就理解了。

      最后处理答案:

      现在以 \(a,b\) 为根的树都处理完了,需要将他们合并。

      结合上面题意的分析,特殊边时,有 \(3\) 中情况:\([1,1],[1,0],[0,1]\)

      对应的,\(ans=f[a][1]\times f[b][1]+f[a][1]\times f[b][0]+f[a][0]\times f[b][1]\)

      就结束了,别忘了 \(\bmod ~P=998244353\)

      最后分析复杂度:不难发现要从 \(1\sim n-1\) 每次都要跑 \(dfs\) 和树形 \(DP\) ,每次都是 \(O(n)\) 的,这样复杂度就成了 \(O(n^2)\)

      点击查看代码
      #include<bits/stdc++.h>
      #define int long long 
      #define endl '\n'
      using namespace std;
      const int N=3e5+10,P=998244353;
      template<typename Tp> inline void read(Tp&x)
      {
          x=0;register bool z=1;
          register char c=getchar();
          for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
          for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
          x=(z?x:~x+1);
      }
      int n,x,y,tx[N],ty[N],f[N][2],ans,father[N];
      vector<int>e[N],son[N];
      void dfs(int x,int fa)
      {
          for(int y:e[x])
          {
              if(y==fa) continue;
              father[y]=x;
              son[x].push_back(y);
              dfs(y,x);
          }
      }
      void dp(int x)
      {
          if(!son[x].size())
          {
              f[x][0]=f[x][1]=1;
              return ;
          }
          for(int y:son[x])
              dp(y),
              (f[x][1]*=f[y][0])%=P,
              (f[x][0]*=(f[y][0]+f[y][1]))%=P;
      }
      signed main()
      {
          #ifndef ONLINE_JUDGE
          freopen("in.txt","r",stdin);
          freopen("out.txt","w",stdout);
          #endif
          read(n);
          for(int i=1;i<n;i++)
              read(x),read(y),
              e[x].push_back(y),
              e[y].push_back(x),
              tx[i]=x,ty[i]=y;
          for(int i=1;i<n;i++)
          {
              int a=tx[i],b=ty[i];
              for(int j=0;j<=n;j++) 
                  father[j]=f[j][0]=f[j][1]=1,
                  son[j].clear();
              dfs(a,b),dp(a);
              dfs(b,a),dp(b);
              ans=(f[a][1]*f[b][1])%P+(f[a][1]*f[b][0])%P+(f[a][0]*f[b][1])%P;
              cout<<ans%P<<endl;
          }
      }
      
  • 正解:

    和上面思路是类似的,但是解决了上面要跑 \(n-1\)\(dfs,DP\) 的问题。

    砍断操作可以用换根 \(DP\) 优化。

    1. 先跑一遍正常的 \(dfs\) 和 树形 \(DP\) ,处理出 \(f[i][0||1]\)

    2. 进行换很 \(DP\)

      \(r[i][1||0]\) 表示以 \(i\) 为整棵树的根节点时选 \(i\) 和不选 \(i\) 的方案数。

      根节点又 \(fa→x\) 时,之前以 \(x\) 为根的子树内对其贡献不变,需要消除之前以 \(x\) 为根的子树对以 \(fa\) 为根的子树的影响,于是得出转移方程:

      \(r[x][1]=f[x][1]\times \dfrac{r[fa][0]}{f[x][1]+f[x][0]};\)

      \(r[x][0]=f[x][0]\times (\dfrac{r[fa][0]}{f[x][0]+f[x][1]}+\dfrac{r[fa][1]}{f[x][0]})\)

    3. 答案处理:

      对于断开的边,默认 \(a\) 为其连接的父亲节点,\(b\) 为其连接的子节点,需要消除以 \(b\) 为根节点子树对于以 \(a\) 为根的树的影响,则为:

      \(ans=\dfrac{r[a][1]}{f[b][0]}\times f[b][0]+\dfrac{r[a][1]}{f[b][0]}\times f[b][1]+\dfrac{r[a][0]}{f[b][1]+f[b][0]}\times f[b][1]\)

    4. 关于 \(\bmod P:\)

      只要是两个数相乘就要 \(\bmod P\),两数相加不必 \(\bmod P\) ,观察仔细,不要漏了一处需要模的地方。

      至于模意义下的除法,用乘法逆元不方便,线性推还会炸空间,可以用快速幂,\(a^{p-2}≡a^1\pmod p\) ,即费马小定理。

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    using namespace std;
    const int N=3e5+10,P=998244353;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    int n,x,y,tx[N],ty[N],f[N][2],r[N][2],ans,father[N];
    vector<int>e[N],son[N];
    void dfs(int x,int fa)
    {
        for(int y:e[x])
        {
            if(y==fa) continue;
            father[y]=x;
            son[x].push_back(y);
            dfs(y,x);
        }
    }
    int qpow(int a,int b,int p)
    {
        int ans=1;
        while(b>0)
        {
            if(b&1) (ans=ans*a)%=p;
            b>>=1;
            (a=a*a)%=p;
        }
        return ans%p;
    }
    void dp(int x)
    {
        if(!son[x].size())
        {
            f[x][0]=f[x][1]=1;
            return ;
        }
        for(int y:son[x])
            dp(y),
            (f[x][1]*=f[y][0])%=P,
            (f[x][0]*=(f[y][0]+f[y][1]))%=P;
    }
    void reroot(int x,int fa)
    {
        if(x!=1)
        {
            int f0=(r[fa][0]*qpow(f[x][0]+f[x][1],P-2,P))%P,
                f1=(r[fa][1]*qpow(f[x][0],P-2,P))%P;
            r[x][0]=(f[x][0]*(f0+f1))%P;
            r[x][1]=(f[x][1]*f0)%P;
        }
        for(int y:e[x])
            if(y!=fa)
                reroot(y,x);
    }
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        read(n);
        for(int i=1;i<n;i++)
            read(x),read(y),
            e[x].push_back(y),
            e[y].push_back(x),
            tx[i]=x,ty[i]=y;
        for(int i=0;i<=n;i++) f[i][1]=f[i][0]=1; 
        dfs(1,0);
        dp(1);
        r[1][0]=f[1][0],r[1][1]=f[1][1];
        reroot(1,0);
        for(int i=1;i<n;i++)
        {
            int a=tx[i],b=ty[i],c;
            if(father[a]==b) c=a, a=b, b=c;
            ans=(((r[a][1]*qpow(f[b][0]%P,P-2,P))%P*f[b][1])%P+r[a][1]+((r[a][0]*qpow(f[b][1]+f[b][0],P-2,P))%P*f[b][1])%P)%P;
            cout<<ans%P<<endl;
        }
    }
    

    另外的思路跳转 @Ishar-zdl

    2024年02月21日21:53:38 ,\(T3\) 的官方题解被教练放进去了,(⊙o⊙)…,就是这份 \(\large{↑}\)

T4 MEX

点击查看抽象的官方题解

image

const int MAXN = 1e6 + 5;
int n, m;
int arr[MAXN];
int last[MAXN];
int nxt[MAXN];
long long answer[MAXN];

struct SegmentTree {
public:
    int min, num;
    long long sum;
    class LazyTag {
    public:
        int cover, tim;
        long long add;
        inline LazyTag() {
            cover = -1;
            add = 0;
            tim = 0;
        }
        inline LazyTag(const int cover): cover(cover) {
            if (cover == -1) {
                tim = 1;
            }
        }
    } tag;
} sgt[MAXN << 2];

inline void PushUp(const int now) {
    sgt[now].min = min(sgt[now << 1].min, sgt[now << 1 | 1].min);
}
void Build(const int now = 1, const int left = 0, const int right = m) {
    if (left == right) {
        sgt[now].num = sgt[now].min = last[left];
        return;
    }

    Build(now << 1, left, (left + right) >> 1);
    Build(now << 1 | 1, ((left + right) >> 1) + 1, right);
    PushUp(now);
}
inline void Down(const SegmentTree::LazyTag tag,
                const int now, const int left, const int right) {
    sgt[now].sum += 1ll * sgt[now].num * tag.tim + tag.add;

    if (~tag.cover) {
        if (~sgt[now].tag.cover) {
            sgt[now].tag.add += 1ll * sgt[now].tag.cover * tag.tim + tag.add;
        } else {
            sgt[now].tag.tim += tag.tim;
            sgt[now].tag.add = tag.add;
        }

        sgt[now].num = sgt[now].min = tag.cover;
        sgt[now].tag.cover = tag.cover;
    } else {
        if (~sgt[now].tag.cover) {
            sgt[now].tag.add += 1ll * sgt[now].tag.cover * tag.tim;
        } else {
            sgt[now].tag.tim += tag.tim;
        }
    }
}
inline void PushDown(const int now, const int left, const int right) {
    Down(sgt[now].tag, now << 1, left, (left + right) >> 1);
    Down(sgt[now].tag, now << 1 | 1, ((left + right) >> 1) + 1, right);
    sgt[now].tag = SegmentTree::LazyTag();
}
void Updata(const int now_left, const int now_right,
            const int cover, const int now = 1,
            const int left = 0, const int right = m) {
    if (now_right < left || right < now_left) {
        return;
    }

    if (now_left <= left && right <= now_right) {
        Down(SegmentTree::LazyTag(cover), now, left, right);
        return;
    }

    PushDown(now, left, right);
    Updata(now_left, now_right, cover, now << 1, left, (left + right) >> 1);
    Updata(now_left, now_right, cover,
        now << 1 | 1, ((left + right) >> 1) + 1, right);
    PushUp(now);
}
inline void Time() {
    Down(SegmentTree::LazyTag(-1), 1, 1, m);
}
int Find(const int now_left, const int now_right,
        const int val, const int now = 1,
        const int left = 0, const int right = m) {
    if (now_right < left || right < now_left || val <= sgt[now].min) {
        return -1;
    }

    if (left == right && now_left <= left && right <= now_right) {
        return left;
    }

    int result(Find(now_left, now_right, val, now << 1 | 1,
                    ((left + right) >> 1) + 1, right));
    return ~result ? result : Find(now_left, now_right,
                                val, now << 1, left, (left + right) >> 1);
}
void GetAnswer(const int now = 1, const int left = 0, const int right = m) {
    if (left == right) {
        answer[left] = sgt[now].sum;
        return;
    }

    PushDown(now, left, right);
    GetAnswer(now << 1, left, (left + right) >> 1);
    GetAnswer(now << 1 | 1, ((left + right) >> 1) + 1, right);
}

int main() {
    scanf("%d%d", &n, &m);
    int last0 = 0;
    long long answer0 = 0;

    for (int i = 1; i <= n; i++) {
        scanf("%d", &arr[i]);

        if (arr[i] == 0) {
            answer0 += (i - last0 - 1ll) * (i - last0) >> 1;
            last0 = i;
        }
    }

    answer0 += (n - last0) * (n + 1ll - last0) >> 1;

    for (int i = 0; i <= m; i++) {
        last[i] = n + 1;
    }

    for (int i = n; i >= 1; i--) {
        nxt[i] = last[arr[i]];
        last[arr[i]] = i;
    }

    for (int i = 1; i <= m - 1; i++) {
        last[i] = max(last[i], last[i - 1]);
    }

    Build();

    for (int i = 1; i <= n; i++) {
        Time();
        int f = Find(arr[i], m, nxt[i]);

        if (~f) {
            Updata(arr[i], f, nxt[i]);
        }
    }

    GetAnswer();
    int l, r;
    scanf("%d%d", &l, &r);

    if (!l) {
        printf("%lld ", answer0);
        l = 1;
    }

    for (int i = l; i <= r; i++) {
        printf("%lld ", answer[i] - answer[i - 1]);
    }

    return 0;
}


点击查看翻译版代码
#include<bits/stdc++.h>
#define int long long
#define N (1000010)
#define ls (p<<1)
#define rs (p<<1|1)
#define I i
#define J j
#define raed read
#define reaD read
#define reAD read
#define rEAD read
#define READ read
#define REAd read
#define REad read
#define Read read
#define Reda read
#define redA read
#define reDA read
#define redA read
#define itn signed
#define Itn signed
#define ITN signed
#define Int signed
#define INT signed
#define foR for
#define fot for
#define foT for
#define sort stable_sort
using namespace std;
namespace IO
{
    #define ll long long
    const int MAX=1<<24;
    char buf[MAX],*p1=buf,*p2=buf;
    char obuf[MAX],*o=obuf;
    #define gc()(p1==p2&&(p2=(p1=buf)\
    +fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
    //template<typename T>
    //inline T read()
    inline int read()
    {
        int x=0;bool f=1;
        char c=gc();
        for(;c<48||c>57;c=gc())if(c=='-')f=0;
        for(;c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c^48);
        return f?x:~x+1;
    }
    void print(ll x){if(x>9)print(x/10);*o++=(x%10)+'0';}
    void pit(ll x){if(x<0)*o++='-',x=~x+1;print(x);}
    void write(ll x,char ed){pit(x);*o++=ed;}
    void flush(){fwrite(obuf,o-obuf,1,stdout);}
    #undef ll
}
using IO::read;using IO::write;using IO::flush;using std::complex;
inline int min(int x,int y){return y&((y-x)>>31)|x&(~(y-x)>>31);}
inline int max(int x,int y){return x&((y-x)>>31)|y&(~(y-x)>>31);}
inline void swap(int &x,int &y){x^=y^=x^=y;}
long long n,m;
void init_set()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
}
int tot,x,y;
struct aa
{
    int nahida,sumeru,panzer;//min num sum
    //min 最小值, num 记录区间和, sum 记录历史和
    struct lazytag
    {
        int furina,Paimon,fucaros;//cover time add
        //cover -1 不覆盖, time 乘法懒标记,记录历史被求和过多少次, add 加法懒标记
        //构造函数,初始化
        inline lazytag()
        {furina=-1,Paimon=fucaros=0;}
        //赋值
        inline lazytag(int furina):furina(furina)
        {if(furina==-1)Paimon=1;}
    }tag;
}t[4000010];
bool tp2=true;
int disco;
int fontaine[1000010];//a
int honkai_star_rail;//last_0
int kamisato_ayaka;//answer_0
int Cyberpunk_2077[1000010];//next
int Soviet[1000010];//last
int honkai[1000010];
inline void pushup(int p)
{
    t[p].nahida=min(t[ls].nahida,t[rs].nahida);
    t[p].sumeru=t[ls].sumeru,t[rs].sumeru;
    t[p].panzer=t[ls].panzer,t[rs].panzer;
}
void build(int p=1,int l=0,int r=m)
{
    if(l==r){t[p].sumeru=t[p].nahida=Soviet[l];return;}
    int mid(l+r>>1);
    build(ls,l,mid),build(rs,mid+1,r);
    pushup(p);
}
inline void down(aa::lazytag tag,int p,int l,int r)
{
    t[p].panzer+=t[p].sumeru*tag.Paimon+tag.fucaros;
    //sum=sum+num*time+add
    if(~tag.furina)
    {
        if(~t[p].tag.furina)//下传 add
            t[p].tag.fucaros+=t[p].tag.furina*tag.Paimon+tag.fucaros;
        else
            t[p].tag.Paimon+=tag.Paimon,//下传 time
            t[p].tag.fucaros+=tag.fucaros;//下传 add
        t[p].sumeru=t[p].nahida=tag.furina;
        t[p].tag.furina=tag.furina;//下传 cover
    }
    else 
    {
        //下传 add
        if(~t[p].tag.furina)t[p].tag.fucaros+=t[p].tag.furina*tag.Paimon;
        //下传 time
        else t[p].tag.Paimon+=tag.Paimon;
    }
}
inline void pushdown(int p,int l,int r)
{
    int mid(l+r>>1);
    down(t[p].tag,ls,l,mid),down(t[p].tag,rs,mid+1,r);
    t[p].tag=aa::lazytag();
}
void update(int p,int ql,int qr,int l,int r,int v)
{
    if(qr<l||r<ql)return;
    if(ql<=l&&r<=qr)
        {down(aa::lazytag(v),p,l,r);return;}
    pushdown(p,l,r);
    int mid(l+r>>1);
    update(ls,ql,qr,l,mid,v),update(rs,ql,qr,mid+1,r,v);
    pushup(p);
}
inline void PaimonDown()
{down(aa::lazytag(-1),1,1,m);}
int search(int p,int ql,int qr,int l,int r,int v)
{
    if(qr<l||r<ql||v<=t[p].nahida)return -1;
    //越界或比该最小值还要小。
    if(l==r&&ql<=l&&r<=qr)return l;
    int mid(l+r>>1);
    int res(search(rs,ql,qr,mid+1,r,v));
    return ~res?res:search(ls,ql,qr,l,mid,v);
}
void SetAns(int p=1,int l=0,int r=m)
{
    if(l==r){honkai[l]=t[p].panzer;return;}
    int mid(l+r>>1);
    pushdown(p,l,r),SetAns(ls,l,mid),SetAns(rs,mid+1,r);
}
signed main()
{
    init_set();
    n=read();m=read();
    for(signed i(1);i<=n;++i)
    {
        fontaine[i]=read();
        if(!fontaine[i])
        {
            kamisato_ayaka+=(i-honkai_star_rail-1)*(i-honkai_star_rail)>>1;//对 0 进行特判。
            honkai_star_rail=i;
        }
        if(fontaine[i]!=i%m)tp2=false;
    }
    if(tp2)//10pts 特判所有元素都为 i%m 的情况
    {
        x=read(),y=read();
        disco=n*(n+1)>>1;
        for(int i(x);i<=y;++i)
        {
            if(i==0)write(((n/m)*((m-1)*m)>>1)+(((n%m))*(n%m+1)>>1),' '),
            disco-=((n/m)*((m-1)*m)>>1)+(((n%m))*(n%m+1)>>1);
            else if(i==m)
                write(disco,' ');
            else write(((n-i+1)/m)*(m-i),' '),disco-=((n-i+1)/m)*(m-i);
        }
        flush();exit(0);
    }
    kamisato_ayaka+=(n-honkai_star_rail)*(n+1-honkai_star_rail)>>1;
    for(int i(0);i<=m;++i)
        Soviet[i]=n+1;//last 数组初始化。
    for(int i(n);i;--i)
        Cyberpunk_2077[i]=Soviet[fontaine[i]],
        //next 数组存下一个 i 的位置 ,由于倒序,所以是下一个。
        Soviet[fontaine[i]]=i;//之后让 last 数组记录上一个。
    //未出现过 i ,last=next=n+1 。
    //只出现一次,last 为第一次出现位置, nxt=n+1
    //出现两次以上 , last 同上, nxt 为第二次出现位置。
    for(int i(1);i<m;++i)
        Soviet[i]=max(Soviet[i],Soviet[i-1]);
    //无论如何, mex 为 i 时,区间内至少也要有 1 至 i-1 。
    build(1,0,m);
    for(int i(1);i<=n;++i)
    {
        PaimonDown();
        int browser(search(1,fontaine[i],m,0,m,Cyberpunk_2077[i]));
        //二分查找在 a[i] 与 m 之间的最大的数位置。
        if(~browser)update(1,fontaine[i],browser,0,m,Cyberpunk_2077[i]);
    }
    SetAns(1,0,m);
    x=read(),y=read();
    if(!x)write(kamisato_ayaka,' '),x=1;
    for(int i(x);i<=y;++i)
        write(honkai[i]-honkai[i-1],' ');
    flush();
    return 0;
}

总结:

题不要读假。

不要随便跳过知识点,抽时间回去把落下的学了。

希望以后的官方题解能及时一点,且码风正常一点,不要再有错的题解。

posted @ 2024-02-21 21:57  卡布叻_周深  阅读(61)  评论(5)    收藏  举报