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\) :精细实现建图。

  • 正解

    • 考虑答案之间的继承关系。
    • \(f_{i,u}\) 表示 \(T_{i}\) 中所有节点到 \(u\) 的距离之和,有 \(ans_{i}=ans_{x}+ans_{y}+w \times siz_{x} \times siz_{y}+f_{x,u} \times siz_{y}+f_{y,v} \times siz_{x}\)
    • 难点在于怎么求 \(f_{i,u}\) 。观察到 \(m \le 300\) ,不妨直接递归暴力计算。类似地,有 \(f_{i,u}=f_{x,u}+f_{y,v}+dis_{i}(u,v) \times siz_{y}\)
    • \(dis_{i}(u,v)\) 同样可以通过拆成三部分相加递归求解。
    • 因有用的 \(dis\) 状态规模为 \(O(m^{3})\) ,记忆化即可。
    点击查看代码
    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

  • \(a_{i}\) 的覆盖区间为 \([l_{i},r_{i}]\) ,等价于询问 \(\max\limits_{i=1}^{n} \{ a_{i}(\min(r_{i},qr)-\max(l_{i},ql)+1)\}\)

  • 部分分

    • \(30pts\) :两遍单调栈处理出 \(l_{i},r_{i}\)\(O(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;
    }
    
  • 正解

    • 即使将式子拆成 \(a_{i}\min(r_{i}-qr+1,r_{i}-l_{i}+1,qr-ql+1,qr-l_{i}+1)\) 也难以维护四个部分的值同时对答案的影响。
    • 直接不管上面的限制,分讨得到 \(\begin{cases} l_{i} \le ql \le qr \le r_{i} \\ ql \le l_{i} \le r_{i} \le qr \\ l_{i} \le ql \le r_{i} \le qr \\ ql \le l_{i} \le qr \le r_{i} \end{cases}\) 四种情况,不妨直接确定最终取值后取 \(\max\)
    • 前两种情况将修改和询问都挂在右端点上,在左端点上进行操作,因符合上述条件的同时也满足 \(i \in [l,r]\) 的限制,故可以直接正反扫描线,单点修改、区间查询线段树维护。
    • 第三种情况其对答案的贡献为 \(a_{i}(r_{i}-ql+1)=a_{i}(r_{i}+1)-a_{i}ql\) 。以 \(-a_{i}\) 为斜率, \(a_{i}(r_{i}+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(n^{2})\) 的做法没过小样例后发现读假题了。
posted @ 2025-01-13 20:46  hzoi_Shadow  阅读(270)  评论(2)    收藏  举报
扩大
缩小