2025省选模拟4

2025省选模拟4

题目来源: 2023 省选联测30

T1 HZTG5872. 枇杷树 0pts

  • 部分分

    • 10pts :模拟建图,统计答案时考虑每条边的贡献。

      点击查看代码
      const ll p=1000000007;
      struct Graph
      {
          ll siz[100010];
          int ans=0;
          vector<pair<int,int> >e[100010];
          void add(int u,int v,int w)
          {
              e[u].push_back(make_pair(v,w));
          }
          void merge(Graph another)
          {   
              for(int i=0;i<=another.siz[0];i++)
              {
                  for(int j=0;j<another.e[i].size();j++)
                  {
                      e[siz[0]+i].push_back(make_pair(another.e[i][j].first+siz[0],another.e[i][j].second));
                  }
              }
          }
          void dfs1(int x,int fa)
          {
              siz[x]=1;
              for(int i=0;i<e[x].size();i++)
              {
                  if(e[x][i].first!=fa)
                  {
                      dfs1(e[x][i].first,x);
                      siz[x]+=siz[e[x][i].first];
                  }
              }
          }
          void dfs2(int x,int fa)
          {
              for(int i=0;i<e[x].size();i++)
              {
                  if(e[x][i].first!=fa)
                  {
                      ans=(ans+(siz[e[x][i].first]%p)*(siz[0]-siz[e[x][i].first])%p*e[x][i].second%p)%p;
                      dfs2(e[x][i].first,x);
                  }
              }
          }
      }d[20];
      int main()
      {
      #define Isaac
      #ifdef Isaac
          freopen("loquat.in","r",stdin);
          freopen("loquat.out","w",stdout);
      #endif
          int m,x,y,u,v,w,i;
          cin>>m;
          d[0].siz[0]=1;
          for(i=1;i<=m;i++)
          {
              cin>>x>>y>>u>>v>>w;
              d[i]=d[x];
              d[i].ans=0;
              d[i].merge(d[y]);
              d[i].add(u,d[i].siz[0]+v,w);
              d[i].add(d[i].siz[0]+v,u,w);
              d[i].dfs1(0,-1);
              d[i].dfs2(0,-1);
              cout<<d[i].ans<<endl;
          }
          return 0;
      }
      
    • 30pts :精细实现建图。

  • 正解

    • 考虑答案之间的继承关系。
    • fi,u 表示 Ti 中所有节点到 u 的距离之和,有 ansi=ansx+ansy+w×sizx×sizy+fx,u×sizy+fy,v×sizx
    • 难点在于怎么求 fi,u 。观察到 m300 ,不妨直接递归暴力计算。类似地,有 fi,u=fx,u+fy,v+disi(u,v)×sizy
    • disi(u,v) 同样可以通过拆成三部分相加递归求解。
    • 因有用的 dis 状态规模为 O(m3) ,记忆化即可。
    点击查看代码
    const ll p=1000000007;
    ll siz[310],l[310],r[310],u[310],v[310],w[310],ans[310];
    unordered_map<ll,unordered_map<ll,ll> >f,dis[310];
    ll get_dis(ll i,ll x,ll y)
    {
        if(x>y)  swap(x,y);
        if(dis[i][x].find(y)!=dis[i][x].end())  return dis[i][x][y];
        if(y<siz[l[i]])  dis[i][x][y]=get_dis(l[i],x,y);
        else  if(x>=siz[l[i]])  dis[i][x][y]=get_dis(r[i],x-siz[l[i]],y-siz[l[i]]);
        else  dis[i][x][y]=((get_dis(l[i],x,u[i])+w[i])%p+get_dis(r[i],y-siz[l[i]],v[i]))%p;
        return dis[i][x][y];
    }
    ll get_f(ll i,ll x)
    {
        if(f[i].find(x)!=f[i].end())  return f[i][x];
        if(x>=siz[l[i]])
        {
            f[i][x]=(get_f(l[i],u[i])+get_f(r[i],x-siz[l[i]]))%p;
            f[i][x]=(f[i][x]+get_dis(i,u[i],x)*(siz[l[i]]%p)%p)%p;
        }
        else
        {
            f[i][x]=(get_f(r[i],v[i])+get_f(l[i],x))%p;
            f[i][x]=(f[i][x]+get_dis(i,v[i]+siz[l[i]],x)*(siz[r[i]]%p)%p)%p;
        }
        return f[i][x];
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("loquat.in","r",stdin);
        freopen("loquat.out","w",stdout);
    #endif
        ll m,i;
        cin>>m;
        siz[0]=1;
        f[0][0]=0;
        dis[0][0][0]=0;
        for(i=1;i<=m;i++)
        {
            cin>>l[i]>>r[i]>>u[i]>>v[i]>>w[i];
            siz[i]=siz[l[i]]+siz[r[i]];
            ans[i]=(ans[l[i]]+ans[r[i]])%p;
            ans[i]=(ans[i]+get_f(l[i],u[i])*(siz[r[i]]%p)%p)%p;
            ans[i]=(ans[i]+get_f(r[i],v[i])*(siz[l[i]]%p)%p)%p;
            ans[i]=(ans[i]+w[i]*(siz[l[i]]%p)%p*(siz[r[i]]%p)%p)%p;
            cout<<ans[i]<<endl;
        }
        return 0;
    }
    

T2 HZTG5873. 上古遗迹 50pts

  • 弱化版: SP1805 HISTOGRA - Largest Rectangle in a Histogram

  • ai 的覆盖区间为 [li,ri] ,等价于询问 maxi=1n{ai(min(ri,qr)max(li,ql)+1)}

  • 部分分

    • 30pts :两遍单调栈处理出 li,riO(n) 处理每组询问。
    • 50pts :对于同一左端点的询问一起处理,需要只跑一遍单调栈。
    点击查看代码(时间复杂度有点假,不建议参考)
    ll a[200010],st[200010],ed[200010],ans[200010];
    stack<ll>s;
    set<ll>t;
    set<ll>::iterator it;
    vector<pair<ll,ll> >q[200010];
    struct SMT
    {
        struct SegmentTree
        {
            ll maxx;
        }tree[800010];
        #define lson(rt) (rt<<1)
        #define rson(rt) (rt<<1|1)
        void pushup(ll rt)
        {
            tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx);
        }
        void clear()
        {
            memset(tree,0,sizeof(tree));
        }
        void update(ll rt,ll l,ll r,ll pos,ll val)
        {
            if(l==r)
            {
                tree[rt].maxx=val;
                return;
            }
            ll mid=(l+r)/2;
            if(pos<=mid)  update(lson(rt),l,mid,pos,val);
            else  update(rson(rt),mid+1,r,pos,val);
            pushup(rt);
        }
        ll query(ll rt,ll l,ll r,ll x,ll y)
        {
            if(x<=l&&r<=y)  return tree[rt].maxx;
            ll mid=(l+r)/2;
            if(y<=mid)  return query(lson(rt),l,mid,x,y);
            if(x>mid)  return query(rson(rt),mid+1,r,x,y);
            return max(query(lson(rt),l,mid,x,y),query(rson(rt),mid+1,r,x,y));
        }
    }T;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("relics.in","r",stdin);
        freopen("relics.out","w",stdout);
    #endif
        ll n,m,l,r,i,j,k;
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=n;i++)  scanf("%lld",&a[i]);
        for(i=1;i<=m;i++)
        {
            scanf("%lld%lld",&l,&r);
            q[l].push_back(make_pair(r,i));
        }
        for(i=1;i<=n;i++)
        {
            sort(q[i].begin(),q[i].end());
            if(q[i].size()!=0)
            {
                T.clear();
                t.clear();
                while(s.empty()==0)  s.pop();
                for(j=0,k=i;j<q[i].size();j++)
                {
                    for(it=t.begin();it!=t.end();it++)
                    {
                        T.update(1,1,n,*it,a[*it]*(q[i][j].first-st[*it]+1));
                    }
                    for(;k<=q[i][j].first;k++)
                    {
                        while(s.empty()==0&&a[s.top()]>=a[k])
                        {
                            ed[s.top()]=k-(a[s.top()]>a[k]);
                            t.erase(s.top());
                            T.update(1,1,n,s.top(),a[s.top()]*(ed[s.top()]-st[s.top()]+1));
                            s.pop();
                        }
                        st[k]=(s.empty()==0)?s.top()+1:i;
                        T.update(1,1,n,k,a[k]*(q[i][j].first-st[k]+1));
                        s.push(k);
                        t.insert(k);
                    }
                    ans[q[i][j].second]=T.query(1,1,n,i,q[i][j].first);
                }
            }
        }
        for(i=1;i<=m;i++)
        {
            printf("%lld\n",ans[i]);
        }
        return 0;
    }
    
  • 正解

    • 即使将式子拆成 aimin(riqr+1,rili+1,qrql+1,qrli+1) 也难以维护四个部分的值同时对答案的影响。
    • 直接不管上面的限制,分讨得到 {liqlqrriqlliriqrliqlriqrqlliqrri 四种情况,不妨直接确定最终取值后取 max
    • 前两种情况将修改和询问都挂在右端点上,在左端点上进行操作,因符合上述条件的同时也满足 i[l,r] 的限制,故可以直接正反扫描线,单点修改、区间查询线段树维护。
    • 第三种情况其对答案的贡献为 ai(riql+1)=ai(ri+1)aiql 。以 ai 为斜率, ai(ri+1) 为截距,查询 ql 处最大值,正着扫描线的过程中李超线段树维护。
    • 第四种情况同理。
    点击查看代码
    #define lson(rt) (rt<<1)
    #define rson(rt) (rt<<1|1)
    struct line
    {
        ll k,b;
    }li[200010];
    ll a[200010],l[200010],r[200010],ans[200010];
    stack<ll>s;
    vector<pair<ll,ll> >q1[200010],q2[200010],c[200010];
    ll f(ll id,ll x)
    {
        return li[id].k*x+li[id].b;
    }
    bool cmp(ll a,ll b,ll x)
    {
        if(f(a,x)-f(b,x)>0)  return true;
        if(f(b,x)-f(a,x)>0)  return false;
        return a<b;
    }
    struct LiChao_Tree
    {
        struct SegmentTree
        {
            ll id;
        }tree[800010];
        void clear()
        {
            memset(tree,0,sizeof(tree));
        }
        void add(ll rt,ll l,ll r,ll id)
        {
            ll mid=(l+r)/2;
            if(cmp(tree[rt].id,id,mid)==false)  swap(tree[rt].id,id);
            if(l==r)  return;
            if(cmp(tree[rt].id,id,l)==false)  add(lson(rt),l,mid,id);
            if(cmp(tree[rt].id,id,r)==false)  add(rson(rt),mid+1,r,id);
        }
        void update(ll rt,ll l,ll r,ll x,ll y,ll id)
        {
            if(x<=l&&r<=y)
            {
                add(rt,l,r,id);
                return;
            }
            ll mid=(l+r)/2;
            if(x<=mid)  update(lson(rt),l,mid,x,y,id);
            if(y>mid)  update(rson(rt),mid+1,r,x,y,id);
        }
        ll query(ll rt,ll l,ll r,ll pos)
        {
            if(l==r)  return f(tree[rt].id,pos);
            ll mid=(l+r)/2;
            if(pos<=mid)  return max(f(tree[rt].id,pos),query(lson(rt),l,mid,pos));
            else  return max(f(tree[rt].id,pos),query(rson(rt),mid+1,r,pos));
        }
    }T;
    struct SMT
    {
        struct SegmentTree
        {
            ll maxx;
        }tree[800010];
        void pushup(ll rt)
        {
            tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx);
        }
        void clear()
        {
            memset(tree,0,sizeof(tree));
        }
        void update(ll rt,ll l,ll r,ll pos,ll val)
        {
            if(l==r)
            {
                tree[rt].maxx=max(tree[rt].maxx,val);
                return;
            }
            ll mid=(l+r)/2;
            if(pos<=mid)  update(lson(rt),l,mid,pos,val);
            else  update(rson(rt),mid+1,r,pos,val);
            pushup(rt);
        }
        ll query(ll rt,ll l,ll r,ll x,ll y)
        {
            if(x<=l&&r<=y)  return tree[rt].maxx;
            ll mid=(l+r)/2,ans=0;
            if(x<=mid)  ans=max(ans,query(lson(rt),l,mid,x,y));
            if(y>mid)  ans=max(ans,query(rson(rt),mid+1,r,x,y));
            return ans;
        }
    }S;
    void solve1(ll n,ll m)
    {
        for(ll i=1;i<=n;i++)  c[i].clear();
        for(ll i=1;i<=n;i++)  c[r[i]].push_back(make_pair(l[i],(r[i]-l[i]+1)*a[i]));
        S.clear();
        for(ll i=1;i<=n;i++)
        {
            for(ll j=0;j<c[i].size();j++)  S.update(1,1,n,c[i][j].first,c[i][j].second);
            for(ll j=0;j<q1[i].size();j++)  ans[q1[i][j].second]=max(ans[q1[i][j].second],S.query(1,1,n,q1[i][j].first,i));
        }
    }
    void solve2(ll n,ll m)
    {
        for(ll i=1;i<=n;i++)  c[i].clear();
        for(ll i=1;i<=n;i++)  c[r[i]].push_back(make_pair(l[i],a[i]));
        S.clear();
        for(ll i=n;i>=1;i--)
        {
            for(ll j=0;j<c[i].size();j++)  S.update(1,1,n,c[i][j].first,c[i][j].second);
            for(ll j=0;j<q1[i].size();j++)  ans[q1[i][j].second]=max(ans[q1[i][j].second],S.query(1,1,n,1,q1[i][j].first)*(i-q1[i][j].first+1));
        }
    }
    void solve3(ll n,ll m)
    {
        for(ll i=1;i<=n;i++)  c[i].clear();
        li[0].b=-0x3f3f3f3f3f3f3f3f;
        for(ll i=1;i<=n;i++)
        {
            li[i]=(line){-a[i],a[i]*(r[i]+1)};
            c[r[i]].push_back(make_pair(l[i],i));
        }
        T.clear();
        for(ll i=1;i<=n;i++)
        {
            for(ll j=0;j<c[i].size();j++)  T.update(1,1,n,c[i][j].first,i,c[i][j].second);
            for(ll j=0;j<q1[i].size();j++)  ans[q1[i][j].second]=max(ans[q1[i][j].second],T.query(1,1,n,q1[i][j].first));
        }
    }
    void solve4(ll n,ll m)
    {
        for(ll i=1;i<=n;i++)  c[i].clear();
        li[0].b=-0x3f3f3f3f3f3f3f3f;
        for(ll i=1;i<=n;i++)
        {
            li[i]=(line){a[i],a[i]*(-l[i]+1)};
            c[l[i]].push_back(make_pair(r[i],i));
        }
        T.clear();
        for(ll i=n;i>=1;i--)
        {
            for(ll j=0;j<c[i].size();j++)  T.update(1,1,n,i,c[i][j].first,c[i][j].second);
            for(ll j=0;j<q2[i].size();j++)  ans[q2[i][j].second]=max(ans[q2[i][j].second],T.query(1,1,n,q2[i][j].first));
        }
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("relics.in","r",stdin);
        freopen("relics.out","w",stdout);
    #endif
        ll n,m,ql,qr,i;
        scanf("%lld%lld",&n,&m);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            while(s.empty()==0&&a[s.top()]>=a[i])  s.pop();
            l[i]=(s.empty()==0)?s.top()+1:1;
            s.push(i);
        }
        while(s.empty()==0)  s.pop();
        for(i=n;i>=1;i--)
        {
            while(s.empty()==0&&a[s.top()]>=a[i])  s.pop();
            r[i]=(s.empty()==0)?s.top()-1:n;
            s.push(i);
        }
        for(i=1;i<=m;i++)
        {
            scanf("%lld%lld",&ql,&qr);
            q1[qr].push_back(make_pair(ql,i));
            q2[ql].push_back(make_pair(qr,i));
        }
        solve1(n,m);
        solve2(n,m);
        solve3(n,m);
        solve4(n,m);
        for(i=1;i<=m;i++)  printf("%lld\n",ans[i]);
        return 0;
    }
    
    

T3 HZTG5874. 吞天得手 20pts

  • 部分分

    • 20pts :爆搜。
    点击查看代码
    const ll p=998244353;
    int a[100010],k;
    vector<int>s;
    struct quality
    {
        vector<int>s;
        bool operator < (const quality &another) const
        {
            for(int i=0;i<min(s.size(),another.s.size());i++)
            {
                if(s[i]<another.s[i])  return true;
                if(s[i]>another.s[i])  return false;
            }
            return s.size()<another.s.size();
        }
    };
    multiset<quality>q;
    multiset<quality>::iterator it;
    void dfs(int pos,int n)
    {
        if(pos==n+1)
        {
            if(s.size()!=0)
            {
                q.insert((quality){s});
                if(q.size()>k) q.erase(--q.end());
            }
            return;
        }
        s.push_back(a[pos]);
        dfs(pos+1,n);
        s.pop_back();
        dfs(pos+1,n);
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("ttds.in","r",stdin);
        freopen("ttds.out","w",stdout);
    #endif
        int n,base,ans,mi,i;
        scanf("%d%d%d",&n,&k,&base);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        dfs(1,n);
        for(it=q.begin();it!=q.end();it++)
        {
            ans=0;
            mi=1;
            for(int i=it->s.size()-1;i>=0;i--)
            {
                ans=(ans+1ll*mi*it->s[i]%p)%p;
                mi=1ll*mi*base%p;
            }
            printf("%d\n",ans);
        }
        return 0;
    }
    
  • 正解

    • 考虑尽可能继承上一个转移而来的字符串。转移实际上是在 DAG 上进行 BFS
    • 考虑分层进行扩展,使用优先队列辅助转移。扩展时枚举后缀中第 x 小值,不断进行回溯直至找满 k 个或后缀已经用完了。
    • 后缀中第 x 小值使用主席树维护,沿途更新哈希值。
    • 具体实现时应将每次相同的数都找出来一起处理。每层维护当前层的最优前缀的结尾位置集合,且这些前缀是相同的。更新时即向下一层提供状态,也向本层提供候选转移状态。每当队列空了或两者的前缀不一样了就进入下一层递归。
    点击查看代码
    const ll p=998244353;
    ll a[100010],n,k,base;
    vector<ll>pos[100010];
    struct PDS_SMT
    {
        ll root[100010],rt_sum=0;
        struct SegmentTree
        {
            ll ls,rs,sum;
        }tree[100010<<5];
        #define lson(rt) (tree[rt].ls)
        #define rson(rt) (tree[rt].rs)
        ll build_rt()
        {
            rt_sum++;
            lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0;
            return rt_sum;
        }
        void update(ll pre,ll &rt,ll l,ll r,ll pos,ll val)
        {
            rt=build_rt();
            tree[rt]=tree[pre];  tree[rt].sum+=val;
            if(l==r)  return;
            ll mid=(l+r)/2;
            if(pos<=mid)  update(lson(pre),lson(rt),l,mid,pos,val);
            else  update(rson(pre),rson(rt),mid+1,r,pos,val);
        }
        pair<ll,ll> query(ll rt1,ll rt2,ll l,ll r,ll k)
        {
            if(l==r)  return make_pair(l,k);
            ll mid=(l+r)/2;
            if(tree[lson(rt2)].sum-tree[lson(rt1)].sum>=k)
            {
                return query(lson(rt1),lson(rt2),l,mid,k);
            }
            else
            {
                return query(rson(rt1),rson(rt2),mid+1,r,k-(tree[lson(rt2)].sum-tree[lson(rt1)].sum));
            }
        }
    }T;
    struct quality
    {
        ll pre,hsh,k,pos;
        bool operator < (const quality &another) const
        {
            return a[pos]>a[another.pos];
        }
    };
    void dfs(priority_queue<quality>&q)
    {
        while(q.empty()==0)
        {
            quality x=q.top(),y;
            priority_queue<quality> nxt;
            while(q.empty()==0&&q.top().hsh==x.hsh)
            {
                x=q.top();  q.pop();
                cout<<x.hsh<<endl;
                k--;
                if(k==0)  exit(0);
                if(x.pos+1<=n)//为下一层提供状态
                {
                    y.pre=x.pos;
                    y.k=1;
                    pair<ll,ll>tmp=T.query(T.root[y.pre],T.root[n],1,100000,y.k);	
                    y.hsh=(x.hsh*base%p+tmp.first)%p;
                    y.pos=pos[tmp.first][lower_bound(pos[tmp.first].begin(),pos[tmp.first].end(),y.pre+1)-pos[tmp.first].begin()+tmp.second-1];
                    nxt.push(y);
                }
                if(x.pre+x.k+1<=n)//为本层提供候选状态,将 k+1 加入集合
                {
                    y.pre=x.pre;
                    y.k=x.k+1;
                    pair<ll,ll>tmp=T.query(T.root[y.pre],T.root[n],1,100000,y.k);	
                    y.hsh=(x.hsh-a[x.pos]+tmp.first+p)%p;
                    y.pos=pos[tmp.first][lower_bound(pos[tmp.first].begin(),pos[tmp.first].end(),y.pre+1)-pos[tmp.first].begin()+tmp.second-1];
                    q.push(y);
                }
            }
            dfs(nxt);
        }
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("ttds.in","r",stdin);
        freopen("ttds.out","w",stdout);
    #endif
        ll minn=0x7f7f7f7f,id=0,i;
        cin>>n>>k>>base;
        priority_queue<quality>q;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
            if(a[i]<minn)
            {
                minn=a[i];
                id=i;
            }
            pos[a[i]].push_back(i);
            T.update(T.root[i-1],T.root[i],1,100000,a[i],1);
        }
        q.push((quality){0,minn,1,id});
        dfs(q);
        return 0;
    }
    

总结

  • 想了一整场的 T1 建虚树后层层递归怎么转移和 T2 怎么一遍单调栈建笛卡尔树。
  • T3 一开始把题读成了求字典序前 k 小的哈希值总和。口胡了个 O(n2) 的做法没过小样例后发现读假题了。
posted @   hzoi_Shadow  阅读(24)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2024-01-13 初中信息奥赛模拟测试
2024-01-13 AT_arc125_c [ARC125C] LIS to Original Sequence 题解
扩大
缩小
点击右上角即可分享
微信分享提示