多校A层冲刺NOIP2024模拟赛26

多校A层冲刺NOIP2024模拟赛26

\(T1\) A. 随机游走 \(100pts/100pts\)

  • 在树上做临项交换即可。

    点击查看代码
    struct node
    {
        ll nxt,to,w;
    }e[500010];
    ll head[500010],v[500010],siz[500010],sum[500010],cnt=0,ans=0,tim=0;
    struct quality
    {
        ll sumt,siz,to,w;
    };
    vector<quality>E[500010];
    bool cmp(quality a,quality b)
    {
        return a.sumt*b.siz<b.sumt*a.siz;
    }
    void add(ll u,ll v,ll w)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        e[cnt].w=w;
        head[u]=cnt;
    }
    void dfs1(ll x)
    {
        siz[x]=v[x];
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            dfs1(e[i].to);
            siz[x]+=siz[e[i].to];
            sum[x]+=sum[e[i].to]+e[i].w;
            E[x].push_back((quality){e[i].w+sum[e[i].to],siz[e[i].to],e[i].to,e[i].w});
        }
        sort(E[x].begin(),E[x].end(),cmp);
    }
    void dfs2(ll x)
    {
        for(ll i=0;i<E[x].size();i++)
        {
            tim+=E[x][i].w;
            ans+=tim*v[E[x][i].to];
            dfs2(E[x][i].to);
        }
    }
    int main()
    {
        freopen("walk.in","r",stdin);
        freopen("walk.out","w",stdout);
        ll n,u,w,i;
        scanf("%lld",&n);
        for(i=2;i<=n;i++)
        {
            scanf("%lld%lld",&u,&w);
            add(u,i,w);
        }
        for(i=1;i<=n;i++)
        {
            cin>>v[i];
        }
        dfs1(1);
        dfs2(1);
        printf("%lld\n",ans);
        return 0;
    }
    

\(T2\) B. 分发奖励 \(68pts/68pts\)

  • 部分分

    • \(68pts\)
      • 先通过 \(DFS\) 序将树上问题转化为序列问题,然后线段树分治套一个支持区间推平的数据结构即可。

      • 赛时犯唐直接写了 \(O(\log n)\) 棵珂朵莉树暴力复制父亲节点信息,且还在好奇为什么改成线段树后 \(n \le 3000\) 的数据点都跑不过去。

        点击查看代码
        struct node
        {
            int nxt,to;
        }e[500010];
        int head[500010],dfn[500010],out[500010],pos[500010],ans[500010],tot=0,cnt=0;
        pair<int,int>val,val1,val2;
        void add(int u,int v)
        {
            cnt++;
            e[cnt].nxt=head[u];
            e[cnt].to=v;
            head[u]=cnt;
        }
        void dfs(int x)
        {
            tot++;
            dfn[x]=tot;
            pos[tot]=x;
            for(int i=head[x];i!=0;i=e[i].nxt)
            {
                dfs(e[i].to);
            }
            out[x]=tot;
        }
        struct ODT
        {
            struct node
            {
                int l,r;
                mutable int col;
                bool operator < (const node &another) const
                {
                    return l<another.l;
                }
            };
            int ans;
            set<node>s;
            void init(int n)
            {
                ans=0;
                s.insert((node){1,n,0});
            }
            set<node>::iterator split(int pos)
            {
                set<node>::iterator it=s.lower_bound((node){pos,0,0});
                if(it!=s.end()&&it->l==pos)
                {
                    return it;
                }
                it--;
                if(it->r<pos)
                {
                    return s.end();
                }
                int l=it->l,r=it->r,col=it->col;
                s.erase(it);
                s.insert((node){l,pos-1,col});
                return s.insert((node){pos,r,col}).first;
            }
            void assign(int l,int r,int col)
            {
                set<node>::iterator itr=split(r+1),itl=split(l);
                for(set<node>::iterator it=itl;it!=itr;it++)
                {
                    ans-=(it->col)*(it->r-it->l+1);
                }
                ans+=r-l+1;
                s.erase(itl,itr);
                s.insert((node){l,r,col});
            }
        }O[25];
        struct SMT
        {
            struct SegmentTree
            {
                vector<pair<int,int> >info;
            }tree[2000010];
            #define lson(rt) (rt<<1)
            #define rson(rt) (rt<<1|1)
            void update1(int rt,int l,int r,int x,int y)
            {
                if(x<=l&&r<=y)
                {
                    tree[rt].info.push_back(val);
                    return;
                }
                int mid=(l+r)/2;
                if(x<=mid)
                {
                    update1(lson(rt),l,mid,x,y);
                }
                if(y>mid)
                {
                    update1(rson(rt),mid+1,r,x,y);
                }
            }	
            void update2(int rt,int l,int r,int x,int y)
            {
                if(x<=l&&r<=y)
                {
                    tree[rt].info.push_back(val1);
                    tree[rt].info.push_back(val2);
                    return;
                }
                int mid=(l+r)/2;
                if(x<=mid)
                {
                    update2(lson(rt),l,mid,x,y);
                }
                if(y>mid)
                {
                    update2(rson(rt),mid+1,r,x,y);
                }
            }	
            void solve(int rt,int l,int r,int dep)
            {
                for(int i=0;i<tree[rt].info.size();i++)
                {
                    O[dep].assign(tree[rt].info[i].first,tree[rt].info[i].second,1);
                }
                if(l==r)
                {
                    ans[pos[l]]=max(O[dep].ans-1,0);
                    return;
                }
                else
                {
                    int mid=(l+r)/2;
                    O[dep+1]=O[dep];
                    solve(lson(rt),l,mid,dep+1);
                    O[dep+1]=O[dep];
                    solve(rson(rt),mid+1,r,dep+1);
                }
            }
        }T;
        int main()
        {
            freopen("reward.in","r",stdin);
            freopen("reward.out","w",stdout);
            int n,m,u,a,b,i;
            scanf("%d%d",&n,&m);
            for(i=2;i<=n;i++)
            {
                scanf("%d",&u);
                add(u,i);
            }
            dfs(1);
            for(i=1;i<=m;i++)
            {
                scanf("%d%d",&a,&b);
                if(a==b)
                {
                    val=make_pair(dfn[a],out[a]);
                    T.update1(1,1,n,dfn[a],out[a]);	
                }
                else
                {
                    val1=make_pair(dfn[a],out[a]);
                    val2=make_pair(dfn[b],out[b]);
                    T.update2(1,1,n,dfn[a],out[a]);
                    T.update2(1,1,n,dfn[b],out[b]);
                }
            }
            O[1].init(n);
            T.solve(1,1,n,1);
            for(i=1;i<=n;i++)
            {
                printf("%d ",ans[i]);
            }
            return 0;
        }
        
  • 正解

    • 转化为序列问题再套线段树分治是不必要的,因为完全可以在原树上再 \(DFS\) 一遍得到相同结果。
    • 可撤销数据结构可以用可持久化平衡树实现珂朵莉树或主席树或线段树维护最小值及最小值出现次数(可以证明最小值不会 \(<0\) )。
    点击查看代码
    struct node
    {
        int nxt,to;
    }e[500010];
    int head[500010],dfn[500010],out[500010],ans[500010],tot=0,cnt=0,n;
    vector<int>q[500010];
    void add(int u,int v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs(int x)
    {
        tot++;
        dfn[x]=tot;
        for(int i=head[x];i!=0;i=e[i].nxt)
        {
            dfs(e[i].to);
        }
        out[x]=tot;
    }
    struct PDS_SMT
    {
        int root[500010],rt_sum;
        struct SegmentTree
        {
            int ls,rs,lazy,sum;
        }tree[500010<<7];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        int build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].lazy=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void pushup(int rt)
        {
            tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum;
        }
        void update(int pre,int &rt,int l,int r,int x,int y)
        {
            rt=build_rt();
            tree[rt]=tree[pre];
            if(tree[rt].lazy==1||(x<=l&&r<=y))
            {
                tree[rt].lazy=1;
                tree[rt].sum=r-l+1;
                return;
            }
            int mid=(l+r)/2;
            if(x<=mid)
            {
                update(lson(pre),lson(rt),l,mid,x,y);
            }
            if(y>mid)
            {
                update(rson(pre),rson(rt),mid+1,r,x,y);
            }
            pushup(rt);
        }
    }T;
    void solve(int x,int fa)
    {
        T.root[x]=T.root[fa];
        for(int i=0;i<q[x].size();i++)
        {
            T.update(T.root[x],T.root[x],1,n,dfn[q[x][i]],out[q[x][i]]);
        }
        ans[x]=max(T.tree[T.root[x]].sum-1,0);
        for(int i=head[x];i!=0;i=e[i].nxt)
        {
            solve(e[i].to,x);
        }
    }
    int main()
    {
        freopen("reward.in","r",stdin);
        freopen("reward.out","w",stdout);
        int m,u,a,b,i;
        scanf("%d%d",&n,&m);
        for(i=2;i<=n;i++)
        {
            scanf("%d",&u);
            add(u,i);
        }
        dfs(1);
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            q[a].push_back(a);
            q[a].push_back(b);
            q[b].push_back(a);
            q[b].push_back(b);
        }
        solve(1,0);
        for(i=1;i<=n;i++)
        {
            printf("%d ",ans[i]);
        }
        return 0;
    }
    

\(T3\) C. 卡路里 \(0pts/0pts\)

  • 部分分

    • 测试点 \(1 \sim 5\) :特殊性质加状压 \(DP\)
    点击查看代码
    ll a[5010][210],d[5010],sum[5010],f[10][(1<<10)+10];
    int main()
    {
        freopen("calorie.in","r",stdin);
        freopen("calorie.out","w",stdout);
        ll n,m,ans=-0x3f3f3f3f,flag=1,num,i,j,k,h,v;
        scanf("%lld%lld",&n,&m);
        for(i=2;i<=m;i++)
        {
            scanf("%lld",&d[i]);
            sum[i]=sum[i-1]+d[i];
        }
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%lld",&a[i][j]);
            }
            for(j=2;j<=n;j++)
            {
                flag&=(a[i][j]==a[i][j-1]);
            }
        }
        if(n>=8)
        {
            ans=0;
            if(flag==1)
            {
                for(i=1;i<=m;i++)
                {
                    ans=max(ans,a[i][1]*n);
                }
            }
            else
            {
                for(i=1;i<=n;i++)
                {
                    ans+=a[1][i];
                }
            }
        }
        else
        {
            memset(f,-0x3f,sizeof(f));
            f[0][0]=0;
            for(i=1;i<=m;i++)
            {
                for(v=0;v<=i-1;v++)
                {
                    for(j=0;j<=(1<<n)-1;j++)
                    {
                        for(k=j;k!=0;k=j&(k-1))
                        {
                            num=0;
                            for(h=0;h<=n-1;h++)
                            {
                                num+=((k>>h)&1)*a[i][h+1];
                            }
                            f[i][j]=max(f[i][j],f[v][j^k]+num-(v!=0)*(sum[i]-sum[v]));
                        }
                    }
                }
                ans=max(ans,f[i][(1<<n)-1]);
            }
        }
        printf("%lld\n",ans);
        return 0;
    }
    
    
  • 正解

    • 观察到最优情况一定形如选择一段区间 \([l,r]\) 然后从某一个端点走到另一个端点,每款奶茶选择区间内卡路里最大的店购买。
    • 不妨钦定从左往右走,并在卡路里相同时选择最靠左的店买奶茶。
    • 单调栈处理出每款奶茶所能覆盖的区间,然后就得到了每款奶茶对于询问区间的贡献。
    • 先对二维平面加差分后再做二维前缀和即可。
    点击查看代码
    ll a[5010][210],d[5010],sum[5010][5010],l[5010],r[5010];
    stack<ll>s;
    int main()
    {
        freopen("calorie.in","r",stdin);
        freopen("calorie.out","w",stdout);
        ll n,m,ans=-0x7f7f7f7f,i,j;
        scanf("%lld%lld",&n,&m);
        for(i=2;i<=m;i++)
        {
            scanf("%lld",&d[i]);
            d[i]+=d[i-1];
        }
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=n;j++)
            {
                scanf("%lld",&a[i][j]);
            }
        }
        for(j=1;j<=n;j++)
        {
            while(s.empty()==0)
            {
                s.pop();
            }
            for(i=1;i<=m;i++)
            {
                while(s.empty()==0&&a[s.top()][j]<=a[i][j])
                {
                    s.pop();
                }
                l[i]=(s.empty()==0)?s.top()+1:1;
                s.push(i);
            }
            while(s.empty()==0)
            {
                s.pop();
            }
            for(i=m;i>=1;i--)
            {
                while(s.empty()==0&&a[s.top()][j]<a[i][j])
                {
                    s.pop();
                }
                r[i]=(s.empty()==0)?s.top()-1:m;
                s.push(i);
            }
            for(i=1;i<=m;i++)
            {
                sum[l[i]][i]+=a[i][j];
                sum[i+1][r[i]+1]+=a[i][j];
                sum[i+1][i]-=a[i][j];
                sum[l[i]][r[i]+1]-=a[i][j];
            }
        }
        for(i=1;i<=m;i++)
        {
            for(j=1;j<=m;j++)
            {
                sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
                if(j>=i)
                {
                    ans=max(ans,sum[i][j]+d[i]-d[j]);	
                }
            }
        }
        printf("%lld\n",ans);
        return 0;
    }
    

\(T4\) D. 传话游戏 \(4pts/4pts\)

总结

  • 赛时后两个半小时基本全在写 \(T3\)\(O(m^{2}3^{n}n)\) 的部分分,最后半个小时才想起来补 \(T3\) 的两个特殊性质和 \(T4\) 的一个特殊性质。
  • \(T3\)int 了,挂了 \(25pts\)

后记

  • \(T2\)
    • 下发的 \(PDF\) 上写的时限为 \(1s\) ,但在 \(OJ\) 上改成了 \(4s\)
    • 数据中没有造 \(a_{i}=b_{i}\) 的特殊性质。
posted @ 2024-11-26 17:56  hzoi_Shadow  阅读(24)  评论(0编辑  收藏  举报
扩大
缩小