2024初三集训模拟测试3

2024初三集训模拟测试3

T1 计蒜客 T3726 排序 0pts

  • 考虑先将 a 数组进行升序排列。然后题意转化为了把 12n 平均分成 2 组,使得每组乘积之和最小;把 2n+14n 平均分成 2 组,使得每组乘积之和最大。

  • 对于 x1,x2,x3,x4 若存在 x1<x2<x3<x4 则有 x1x2+x3x4>x1x3+x2x4>x1x4+x2x3 ,若加上等于号则有 x1x2+x3x4x1x3+x2x4x1x4+x2x3

  • 故最终 i=2n+14n[imod2=1]×xixi+1i=1nxix2ni+1 即为所求。

    点击查看代码
    ll a[500001];
    int main()
    {
        ll n,ans=0,i;
        cin>>n;
        for(i=1;i<=4*n;i++)
        {
            cin>>a[i];
        }
        sort(a+1,a+1+4*n);
        for(i=1;i<=n;i++)
        {
            ans-=a[i]*a[2*n-i+1];
        }
        for(i=2*n+1;i<=4*n;i+=2)
        {
            ans+=a[i]*a[i+1];
        }
        cout<<ans<<endl;
        return 0;
    }
    

T2 计蒜客 T3727 牛吃草 0pts

  • 部分分

    • 90pts
      • 为使最小覆盖大小最大,考虑二分答案,设当前二分出来的答案为 mid

      • fi 表示对 [1,i] 完成最小覆盖大小 mid 的覆盖后的最大总覆盖大小,状态转移方程为 fi=max(fi1,maxj=iwiimid{fj+(ij)})=max(fi1,maxj=iw[i]imid{fjj}+i) 。暴力进行转移即可。

        点击查看代码
        int w[600000],f[600000];
        bool check(int mid,int n,int m)
        {
            int i,j;
            memset(f,0,sizeof(f));
            for(i=1;i<=n;i++)
            {
                f[i]=f[i-1];
                if(i-w[i]>=0)
                {
                    for(j=i-w[i];j<=i-mid;j++)
                    {
                        f[i]=max(f[i],f[j]+(i-j));
                    }
                }
            }
            return f[n]>=m;
        }
        int main()
        {
            int n,m,s,l=1,r,mid,i;
            cin>>n;
            for(i=1;i<=n;i++)
            {
                cin>>w[i];
            }
            cin>>s;
            r=m=ceil(1.0*n*s/100);
            while(l<=r)
            {
                mid=(l+r)/2;
                if(check(mid,n,m)==true)
                {
                    l=mid+1;
                }
                else
                {
                    r=mid-1;
                }
            }
            cout<<r<<endl;
            return 0;
        }
        
  • 正解

    • 由于 wi1wi1 ,故 (i1)wi1iwi ,故当 i 增大时,有 j 的上界增加了 1 ,下界减少了 wi1(wi1) ,使用单调队列维护即可。
    点击查看代码
    int w[600000],f[600000];
    bool check(int mid,int n,int m)
    {
        deque<int>q;
        for(int i=1;i<=n;i++)
        {
            f[i]=f[i-1];
            if(i-mid>=0)
            {
                while(q.empty()==0&&f[q.back()]-q.back()<=f[i-mid]-(i-mid))
                {
                    q.pop_back();
                }
                q.push_back(i-mid);
                while(q.empty()==0&&q.front()<=i-w[i]-1)
                {
                    q.pop_front();
                }
                if(q.empty()==0)
                {
                    f[i]=max(f[i],f[q.front()]-q.front()+i);
                }
            }
        }
        return f[n]>=m;
    }
    int main()
    {
        int n,m,s,l=1,r,mid,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>w[i];
        }
        cin>>s;
        r=n;
        m=ceil(1.0*n*s/100);
        check(1,n,m);
        while(l<=r)
        {
            mid=(l+r)/2;
            if(check(mid,n,m)==true)
            {
                l=mid+1;
            }
            else
            {
                r=mid-1;
            }
        }
        cout<<r<<endl;
        return 0;
    }
    

T3 计蒜客 T3728 树上的宝藏 0pts

  • 部分分

    • 60pts :设 fx,0,fx,1 分别表示以 x 为根节点的子树内,不选和选 x 的方案数。状态转移方程为 {fx,0=ySon(x)(fy,0+fy,1)fx,1=ySon(x)fy,0 。对于一条边 (x,y) ,砍断这条边后,进行树形 DP ,此时 fx,0fy,1+fx,1fy,0+fx,1fy,1 即为所求。
  • 正解

    • 发现砍断操作可以用换根 DP 优化。
      • 钦定 1 为根节点。
      • 第一遍 DFS 时,处理出 fx,0,fx,1
      • 第二遍 DFS 时,进行换根。设 gx,0,gx,1 表示以 x 为整棵树的根节点时,不选和选 x 的方案数,仍钦定 1 为根节点。考虑根节点由 fax 变为 x(x1) 的过程中,以 x 为根的子树内的节点对答案的贡献不变,要消除以 x 为根的子树内的节点对以 fax 为根的子树的影响,故状态转移方程为 {gx,0=fx,0×(gfax,0fx,0+fx,1+gfax,1fx,0)gx,1=fx,1×gfax,0fx,0+fx,1
    • 对于一条边 (x,y) ,进行分类讨论。
      • 若不选 x ,选 y ,则有 fy,1×gx,0fy,0+fy,1 即为所求。
      • 若选 x ,不选 y ,则有 fy,0×gx,1fy,0=gx,1 即为所求。
      • 若选 x,y ,则有 fy,1×gx,1fy,0 即为所求。
    点击查看代码
    const ll p=998244353;
    struct node
    {
        ll nxt,to;
    }e[700000];
    ll head[700000],dep[700000],u[700000],v[700000],g[700000][2],f[700000][2],cnt=0;
    void add(ll u,ll v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    ll qpow(ll a,ll b,ll p)
    {
        ll ans=1;
        while(b>0)
        {
            if(b&1)
            {
                ans=ans*a%p;
            }
            b>>=1;
            a=a*a%p;
        }
        return ans;
    }
    void dfs(ll x,ll fa)
    {
        f[x][0]=f[x][1]=1;
        dep[x]=dep[fa]+1;
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=fa)
            {
                dfs(e[i].to,x);
                f[x][0]=f[x][0]*((f[e[i].to][0]+f[e[i].to][1])%p)%p;
                f[x][1]=f[x][1]*f[e[i].to][0]%p;
            }
        }
    }
    void reroot(ll x,ll fa)
    {
        if(x!=1)
        {
            ll f0=g[fa][0]*qpow((f[x][0]+f[x][1])%p,p-2,p)%p,f1=g[fa][1]*qpow(f[x][0],p-2,p)%p;
            g[x][0]=f[x][0]*((f0+f1)%p)%p;
            g[x][1]=f[x][1]*f0%p;
        }
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            if(e[i].to!=fa)
            {
                reroot(e[i].to,x);
            }
        }
    }
    int main()
    {
        ll n,sum1,sum2,sum3,i;
        cin>>n;
        for(i=1;i<=n-1;i++)
        {
            cin>>u[i]>>v[i];
            add(u[i],v[i]);
            add(v[i],u[i]);
        }
        dfs(1,0);
        g[1][0]=f[1][0];
        g[1][1]=f[1][1];
        reroot(1,0);
        for(i=1;i<=n-1;i++)
        {
            if(dep[v[i]]<dep[u[i]])
            {
                swap(u[i],v[i]);
            }
            sum1=((f[v[i]][1]*g[u[i]][0])%p)*qpow((f[v[i]][0]+f[v[i]][1])%p,p-2,p)%p;
            sum2=g[u[i]][1];
            sum3=((f[v[i]][1]*g[u[i]][1])%p)*qpow(f[v[i]][0]%p,p-2,p)%p;
            cout<<(((sum1+sum2)%p)+sum3)%p<<endl;
        }
        return 0;
    }
    

T4 计蒜客 T3729 MEX 0pts

  • Special Judgeluogu P4137 Rmq Problem / mex

  • 部分分

    • 10pts

      • 对于每个区间枚举左右端点,进行暴力求解。

        点击查看代码
        ll a[5000000],vis[5000000],ans[5000000];
        int main()
        {
            ll n,m,p,q,l,r,i;
            cin>>n>>m;
            for(i=1;i<=n;i++)
            {
                cin>>a[i];
            }
            for(l=1;l<=n;l++)
            {
                for(r=l;r<=n;r++)
                {
                    for(i=l;i<=r;i++)
                    {
                        vis[a[i]]=1;
                    }
                    for(i=0;i<=m;i++)
                    {
                        if(vis[i]==0)
                        {
                            ans[i]++;
                            break;
                        }
                    }
                    for(i=0;i<=m;i++)
                    {
                        vis[i]=0;
                    }
                }
            }
            cin>>p>>q;
            for(i=p;i<=q;i++)
            {
                cout<<ans[i]<<" ";
            }
            return 0;
        }
        
    • 20pts

      • 使用莫队进行统计答案。

        点击查看代码
        int a[3000010],cnt[3000010],lscnt[3000010],ans[3000010],L[3000010],R[3000010],pos[3000010],klen,ksum;
        struct ask
        {
            int l,r,id;
        }q[3000010];
        bool q_cmp(ask a,ask b)
        {
            return (pos[a.l]==pos[b.l])?(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 cnt[],int x)
        {
            cnt[a[x]]++;
        }
        void del(int cnt[],int x,int &sum)
        {
            cnt[a[x]]--;
            sum=(cnt[a[x]]==0)?min(sum,a[x]):sum;
        }
        int main()
        {
            int n,mm,m=0,x,y,l=1,r,anss=0,sum=0,tmp=0,lastblock=0,i,j;
            cin>>n>>mm;
            r=n;
            for(i=1;i<=n;i++)
            {
                cin>>a[i];
            }
            for(i=1;i<=n;i++)
            {
                for(j=i;j<=n;j++)
                {
                    m++;
                    q[m].l=i;
                    q[m].r=j;
                    q[m].id=m;
                }
            }
            init(n,m);
            sort(q+1,q+1+m,q_cmp);
            for(i=1;i<=n;i++)
            {
                add(cnt,i);
            }
            while(cnt[anss]>=1)
            {
                anss++;
            }
            for(i=1;i<=m;i++)   
            {
                if(pos[q[i].l]==pos[q[i].r])
                {
                    sum=0;
                    for(j=q[i].l;j<=q[i].r;j++)
                    {
                        add(lscnt,j);
                    }
                    while(lscnt[sum]>=1)
                    {
                        sum++;
                    }
                    for(j=q[i].l;j<=q[i].r;j++)
                    {
                        lscnt[a[j]]--;
                    }
                }
                else
                {
                    if(lastblock!=pos[q[i].l])
                    {
                        while(r<n)
                        {
                            r++;
                            add(cnt,r);
                        }
                        while(l<L[pos[q[i].l]])
                        {
                            del(cnt,l,anss);
                            l++;
                        }
                        tmp=anss;
                        lastblock=pos[q[i].l];
                    }
                    while(r>q[i].r)
                    {
                        del(cnt,r,tmp);
                        r--;
                    }
                    sum=tmp;
                    while(l<q[i].l)
                    {
                        del(cnt,l,sum);
                        l++;
                    }
                    while(l>L[pos[q[i].l]])
                    {
                        l--;
                        add(cnt,l);
                    }
                }
                ans[sum]++;
            }
            cin>>x>>y;
            for(i=x;i<=y;i++)
            {
                cout<<ans[i]<<" ";
            }
            return 0;
        }
        
    • 30pts

      点击查看 APJifengc 写的题解

  • 正解

    点击查看 APJifengc 写的题解

    原来的 std

    #include <bits/stdc++.h>
    using namespace std;
    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) {
                        add=tim=0;// Linux 下会出莫名错误,遂把这句话补上了
                        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;
    }
    
    点击查看代码
    ll a[1000010],ans[1000010],last[1000010],nxt[1000010];
    struct SMT
    {
        struct SegmentTree
        {
            ll len,minn,sum,hsum,cover,tlazy,hlazy;
        }tree[4000010];
        #define lson(rt) (rt<<1)
        #define rson(rt) (rt<<1|1)
        void pushup(ll rt)
        {
            tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn);
            tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
            tree[rt].hsum=tree[lson(rt)].hsum+tree[rson(rt)].hsum;
        }
        void build(ll rt,ll l,ll r)
        {
            tree[rt].len=r-l+1;
            tree[rt].cover=-1;  tree[rt].tlazy=tree[rt].hlazy=0;
            if(l==r)
            {
                tree[rt].minn=tree[rt].sum=last[l];
                return;
            }
            ll mid=(l+r)/2;
            build(lson(rt),l,mid);  build(rson(rt),mid+1,r);
            pushup(rt);
        }
        void pushlazy(ll rt,ll cover,ll tlazy,ll hlazy)
        {
            tree[rt].hsum+=tree[rt].sum*tlazy+tree[rt].len*hlazy;
            tree[rt].hlazy+=hlazy;
            if(tree[rt].cover==-1)  tree[rt].tlazy+=tlazy;
            else  tree[rt].hlazy+=tree[rt].cover*tlazy;
            if(cover!=-1)
            {
                tree[rt].sum=tree[rt].len*cover;
                tree[rt].minn=tree[rt].cover=cover;
            }
        }
        void pushdown(ll rt)
        {
            pushlazy(lson(rt),tree[rt].cover,tree[rt].tlazy,tree[rt].hlazy);
            pushlazy(rson(rt),tree[rt].cover,tree[rt].tlazy,tree[rt].hlazy);
            tree[rt].cover=-1;  tree[rt].tlazy=tree[rt].hlazy=0;
        }
        void update(ll rt,ll l,ll r,ll x,ll y,ll val)
        {
            if(x<=l&&r<=y)
            {
                pushlazy(rt,val,0,0);
                return;
            }
            pushdown(rt);
            ll mid=(l+r)/2;
            if(x<=mid)  update(lson(rt),l,mid,x,y,val);
            if(y>mid)  update(rson(rt),mid+1,r,x,y,val);
            pushup(rt);
        }
        ll query(ll rt,ll l,ll r,ll x,ll y,ll val)
        {
            if(y<l||r<x||val<=tree[rt].minn)  return -1;
            if(x<=l&&r<=y&&l==r)  return l;
            ll mid=(l+r)/2,pos=query(rson(rt),mid+1,r,x,y,val);
            return (pos==-1)?query(lson(rt),l,mid,x,y,val):pos;
        }
        void ask(ll rt,ll l,ll r)
        {
            if(l==r)  
            {
                ans[l]=tree[rt].hsum;
                return;
            }
            pushdown(rt);
            ll mid=(l+r)/2;
            ask(lson(rt),l,mid);  ask(rson(rt),mid+1,r);
        }
    }T;
    int main()
    {
    // #define Isaac
    #ifdef Isaac
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        ll n,m,l,r,_ans=0,_last=0,pos,i;
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            if(a[i]==0)
            {
                _ans+=(i-_last-1)*(i-_last)/2;  _last=i;
            }
        }
        scanf("%lld%lld",&l,&r);
        _ans+=(n-_last)*(n+1-_last)/2;
        for(i=0;i<=m;i++)  last[i]=n+1;
        for(i=n;i>=1;i--)
        {
            nxt[i]=last[a[i]];  last[a[i]]=i;
        }
        for(i=1;i<=m-1;i++)  last[i]=max(last[i],last[i-1]);
        T.build(1,0,m);
        for(i=1;i<=n;i++)
        {
            T.pushlazy(1,-1,1,0);
            pos=T.query(1,0,m,a[i],m,nxt[i]);
            if(pos!=-1)  T.update(1,0,m,a[i],pos,nxt[i]);
        }
        T.ask(1,0,m);
        if(l==0)
        {
            printf("%lld ",_ans);
            l=1;
        }
        for(i=l;i<=r;i++)  printf("%lld ",ans[i]-ans[i-1]);
        return 0;
    }
    

总结

  • T1 场上结论没有猜出来,导致心态炸了。
  • T2 场上只看出来了二分答案,没有再接着去想,而且貌似还读错题了。
  • T3 场上读假题了。
  • T4 场上部分分没打,亏了。
posted @   hzoi_Shadow  阅读(50)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
扩大
缩小
点击右上角即可分享
微信分享提示