2025多校冲刺省选模拟赛1

2025多校冲刺省选模拟赛1

T1 A. 切割蛋糕(cake) 100pts

  • sumi=sumi1+ai

  • Alice 选择了 x 块蛋糕,总和为 s ,则限制条件为 sxsumnsnx ,移项得到 snsumnx

  • 设以 i 为起点,则需要保证 {j[i,n],(sumjsumi)sumn(ji+1)j[1,i2],(sumn+sumjsumi1)sumn(ni+j+1) ,移项后得到 sumjnsumnjsumi1nsumni+sumn ,分别前后缀取 min 判断即可。

    • 需要边界特判 i=1 时可选的只有 [1,n1]i=2 时可选的只有 [2,n] ,但数据貌似没卡。
    点击查看代码
    ll a[500010],sum[500010],flag[2][500010];
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("cake.in","r",stdin);
        freopen("cake.out","w",stdout);
    #endif
        ll n,ans=-1,minn=0x7f7f7f7f7f7f7f7f,i;
        scanf("%lld",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(i=n;i>=1;i--)
        {
            minn=min(minn,sum[i]*n-sum[n]*i);
            flag[0][i]=(minn>=sum[i-1]*n-sum[n]*i+sum[n]);
        }
        minn=0x7f7f7f7f7f7f7f7f;
        for(i=n-1;i>=1;i--)
        {
            minn=min(minn,sum[i]*n-sum[n]*i);
        }
        flag[0][1]=flag[1][1]=(minn>=0);
        flag[1][2]=1;
        minn=0x7f7f7f7f7f7f7f7f;
        for(i=1;i<=n;i++)
        {
            minn=min(minn,sum[i]*n-sum[n]*i);
            flag[1][i+2]=(minn>=sum[i+2-1]*n-sum[n]*(i+2)+sum[n]);
        }
        for(i=1;i<=n;i++)
        {
            if(flag[0][i]==1&&flag[1][i]==1)
            {
                ans=i;
                break;
            }
        }
        printf("%lld\n",ans);
        return 0;
    }
    

T2 B. 游乐园(park) 60pts

  • 原题: luogu P3045 [USACO12FEB] Cow Coupons G

  • 反悔贪心,因后面替换前面的 bj 时可以保留 bjaj ,否则直接删掉 bj ,故需要额外开两个小根堆存储当前未被使用 a,b 的贡献,需要懒惰删除。

    点击查看代码
    ll vis[200010];
    pair<ll,ll>a[200010];
    priority_queue<pair<ll,ll>,vector<pair<ll,ll> >,greater<pair<ll,ll> > >q1,q2,q3;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("park.in","r",stdin);
        freopen("park.out","w",stdout);
    #endif
        ll n,k,t,sum=0,ans=0,i;
        cin>>n>>k>>t;
        for(i=1;i<=n;i++)	
        {
            cin>>a[i].second>>a[i].first;
        }
        sort(a+1,a+1+n);
        for(i=1;i<=n;i++)
        {
            if(q1.size()>=k||sum+a[i].first>t)
            {
                q2.push(make_pair(a[i].first,i));
                q3.push(make_pair(a[i].second,i));
            }
            else
            {
                if(sum+a[i].first<=t)
                {
                    sum+=a[i].first;
                    ans++;
                    q1.push(make_pair(a[i].second-a[i].first,i));
                }
            }
        }
        for(i=q1.size()+1;i<=n;i++)
        {
            while(q2.empty()==0&&vis[q2.top().second]==1)
            {
                q2.pop();
            }
            while(q3.empty()==0&&vis[q3.top().second]==1)
            {
                q3.pop();
            }
            if(q1.empty()==0)
            {
                if(sum+min(q3.top().first,q2.top().first+q1.top().first)<=t)
                {
                    sum+=min(q3.top().first,q2.top().first+q1.top().first);
                    ans++;
                }
                if(q3.top().first<q2.top().first+q1.top().first)
                {
                    vis[q3.top().second]=1;
                    q3.pop();
                }
                else
                {
                    vis[q2.top().second]=1;
                    q1.pop();
                    q1.push(make_pair(a[q2.top().second].second-q2.top().first,q2.top().second));
                    q2.pop();
                }
            }	
            else
            {
                if(sum+q3.top().first<=t)
                {
                    sum+=q3.top().first;
                    ans++;
                }
                vis[q3.top().second]=1;
                q3.pop();
            }
        }
        cout<<ans<<endl;
        return 0;
    }
    
  • 放两组 hack 数据。

    点击查看数据 1
    in:
    10 1 8467
    7058 57
    4082 2989
    1015 273
    9569 2307
    219 217
    1953 433
    8659 5494
    4289 2050
    3391 1301
    9734 3199
    
    ans:
    5
    
    点击查看数据 2
    in:
    5 3 13
    17 16
    18 15
    19 14
    17 14
    17 15
    
    ans:
    0
    

T3 C. 有根树(tree) 10pts

  • 部分分

    • 10pts :枚举全排列。

      点击查看代码
      const ll mod=998244353;
      struct node
      {
          ll nxt,to;
      }e[500010];
      ll head[500010],fa[500010],col[2][500010],p[500010],cnt=0;
      void add(ll u,ll v)
      {
          cnt++;
          e[cnt].nxt=head[u];
          e[cnt].to=v;
          head[u]=cnt;
      }
      void access(ll x,ll id)
      {
          for(ll i=head[x];i!=0;i=e[i].nxt)
          {
              col[id][i]=0;
          }
          for(;x!=1;x=fa[x])
          {
              for(ll i=head[fa[x]];i!=0;i=e[i].nxt)
              {
                  col[id][i]=(e[i].to==x);
              }
          }
      }
      ll ask(ll n)
      {
          ll ans=0,flag;
          for(ll i=1;i<=n;i++)
          {
              p[i]=i;
          }
          do
          {
              flag=1;
              fill(col[1]+1,col[1]+1+cnt,0);
              for(ll i=1;i<=n;i++)
              {
                  access(p[i],1);
              }
              for(ll i=1;i<=cnt&&flag==1;i++)
              {
                  flag&=(col[0][i]==col[1][i]);
              }
              ans=(ans+flag)%mod;
          }while(next_permutation(p+1,p+1+n));
          return ans;
      }
      int main()
      {
      #define Isaac
      #ifdef Isaac
          freopen("tree.in","r",stdin);
          freopen("tree.out","w",stdout);
      #endif
          ll n,m,x,i;
          scanf("%lld%lld",&n,&m);
          for(i=2;i<=n;i++)
          {
              scanf("%lld",&fa[i]);
              add(fa[i],i);
          }
          for(i=1;i<=m;i++)
          {
              scanf("%lld",&x);
              access(x,0);
              printf("%lld\n",ask(n));
          }
          return 0;
      }
      
  • 正解

    • 由实链剖分和 access 操作定义可知操作过程中每个点连向儿子的边中最多有一条实边。
    • 定义一个节点也是一条实链。
    • 观察到一条极长实链 x1x2xk 中链底节点 xk 一定是链上节点中最后一次被操作的,其他节点的顺序随意。同时 x1fax1 所在实链链底节点 p 操作靠前。对这棵树进行重构,非链底节点向所在链底连边,链底节点向父亲所在链底连边,限制条件转化为儿子节点比父亲节点先操作。
    • 此时转化为了 树的拓扑序计数 ,易得递推式 fx=(sizx1sizy1,sizy2,,sizy|Son(x)|)ySon(x)fy=(sizx1)!ySon(x)fysizy!=sizx!sizxySon(x)sizy!sizyzSon(y)fzsizz!sizy!=sizx!ySubtree(x)sizy ,全局答案为 f1=n!i=1nsizi
    • 在上面的式子中,重构出的树只有原树上的链底节点的 siz 不为 1 (不考虑只有一个点的实链)且等于链顶节点在原树上的 siz 。等价于查询 n!xSsizx ,其中 S 为链顶集合。难点在于如何维护链顶集合 S
    • 此时 LCT 辅助换根 DP 已经很容易维护了,详见 [ABC160F] Distributing Integers 。考虑树剖怎么维护。
    • 每次 access 操作对一条重链的影响是若干段的链顶被清空,每段的结尾被加入 S 。故可以对每条重链开一个栈手动模拟 set 自浅到深维护极长实链段,由颜色端均摊理论可知时间复杂度为 O(nlogn)
    • 具体实现时,每个极长实链段分别维护其在这条重链的开头、实链在这条重链上的转折点(若没有另一条与其相连重链则记为原值,否则记为另一条与其相连的重链链顶)、实链的结尾,每个节点分别维护其所在实链链顶。
    • 清空时分讨完全包含或部分包含,前者要特判结尾分裂产生的影响,后者直接分裂成两部分。
    点击查看代码
    const ll p=998244353;
    struct node
    {
        ll nxt,to;
    }e[500010];
    ll head[500010],fa[500010],inv[500010],siz[500010],son[500010],dep[500010],top[500010],col_top[500010],cnt=0,ans=1;
    struct quality
    {
        mutable ll l;
        ll r,ed;
    }it;
    stack<quality>s[500010];
    void add(ll u,ll v)
    {
        cnt++;
        e[cnt].nxt=head[u];
        e[cnt].to=v;
        head[u]=cnt;
    }
    void dfs1(ll x)
    {
        siz[x]=1;
        dep[x]=dep[fa[x]]+1;
        for(ll i=head[x];i!=0;i=e[i].nxt)
        {
            dfs1(e[i].to);
            siz[x]+=siz[e[i].to];
            son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
        }
        ans=ans*x%p*inv[siz[x]]%p;
    }
    void dfs2(ll x,ll id)
    {
        top[x]=id;
        col_top[x]=x;
        if(son[x]!=0)
        {
            dfs2(son[x],id);
            for(ll i=head[x];i!=0;i=e[i].nxt)
            {
                if(e[i].to!=son[x])
                {
                    dfs2(e[i].to,e[i].to);
                }
            }
        }	
        s[top[x]].push((quality){x,x,x});
    }
    void update(ll x)
    {
        ans=ans*inv[siz[1]]%p;
        ll id=x,last=x;
        while(x!=0)
        {
            while(s[top[x]].empty()==0&&dep[s[top[x]].top().l]<=dep[x])
            {
                it=s[top[x]].top();
                if(dep[it.r]-(top[it.r]!=top[x])>dep[x])//不完全包含,需要分裂
                //-(top[it.r]!=top[x]) 是为了找到在本条重链上的转折点
                {
                    ans=ans*siz[col_top[it.ed]]%p*inv[siz[son[x]]]%p;
                    s[top[x]].top().l=col_top[it.ed]=son[x];
                }
                else
                {
                    if(dep[col_top[it.ed]]<=dep[it.l])//完全包含时在首次遍历到这条实链后就分裂开更新答案
                    {
                        ans=ans*siz[col_top[it.ed]]%p*((top[it.r]!=top[x])?inv[siz[it.r]]:1)%p;
                        //top[it.r]!=top[x] 对应有其他重链连接,否则由极长实链可知其不会产生影响
                        col_top[it.ed]=it.r;
                    }
                    s[top[x]].pop();
                }
            }
            s[top[x]].push((quality){top[x],last,id});//last记录另一条与其相交的重链链顶
            last=top[x];
            x=fa[top[x]];
        }
        col_top[id]=1;
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
    #endif
        ll n,m,x,i;
        scanf("%lld%lld",&n,&m);
        inv[1]=1;
        for(i=2;i<=n;i++)
        {
            scanf("%lld",&fa[i]);
            add(fa[i],i);
            inv[i]=(p-p/i)*inv[p%i]%p;
        }
        dfs1(1);
        dfs2(1,1);
        for(i=1;i<=m;i++)
        {
            scanf("%lld",&x);
            update(x);
            printf("%lld\n",ans);
        }
        return 0;
    }
    

T4 D. 集合操作(set) 10pts

  • 弱化版: HihoCoder - 1759 Pick Numbers

  • 部分分

    • 10pts :爆搜。

      点击查看代码
      ll p,ans=0;
      deque<ll>s[1010],q;
      ll qpow(ll a,ll b,ll p)
      {
          ll ans=1;
          while(b)
          {
              if(b&1)
              {
                  ans=ans*a%p;
              }
              b>>=1;
              a=a*a%p;
          }
          return ans;
      }
      void dfs(ll dep,ll w,ll mul)
      {
          if(dep!=0)
          {
              s[dep]=s[dep-1];
              for(ll i=0;i<s[dep].size();i++)
              {
                  if(s[dep][i]%w==0)
                  {
                      q.push_back(i);
                  }
              }
              while(q.empty()==0)
              {
                  s[dep].erase(q.back()+s[dep].begin());
                  q.pop_back();
              }
          }
          if(s[dep].size()==0)
          {
              ans=(ans+dep*mul%p)%p;
              return;
          }
          mul=mul*qpow(s[dep].size(),p-2,p)%p;
          for(ll i=0;i<s[dep].size();i++)
          {
              dfs(dep+1,s[dep][i],mul);
          }
      }
      int main()
      {
      #define Isaac
      #ifdef Isaac
          freopen("set.in","r",stdin);
          freopen("set.out","w",stdout);
      #endif
          ll n,i;
          cin>>n>>p;
          for(i=1;i<=n;i++)
          {
              s[0].push_back(i);
          }
          dfs(0,0,1);
          cout<<ans<<endl;
          return 0;
      }
      
    • 60pts

      • 类似 CF280C Game on Tree ,依据期望线性性,考虑每个数被用于增加次数的期望,得到 i=1nE(i)=i=1n1d(i) 即为所求。

      • 线性筛或暴力求即可。

        点击查看代码
        ll p,prime[100010],f[100010],low[100010],nu[100010],vis[100010],len=0;
        ll qpow(ll a,ll b,ll p)
        {
            ll ans=1;
            while(b)
            {
                if(b&1)
                {
                    ans=ans*a%p;
                }
                b>>=1;
                a=a*a%p;
            }
            return ans;
        }
        void isprime(ll n)
        {
            memset(vis,0,sizeof(vis));
            f[1]=1;
            for(ll i=2;i<=n;i++)
            {
                if(vis[i]==0)
                {
                    len++;
                    prime[len]=i;
                    nu[i]=1;
                    low[i]=i;
                    f[i]=qpow(2,p-2,p);
                }
                for(ll j=1;j<=len&&i*prime[j]<=n;j++)
                {
                    vis[i*prime[j]]=1;
                    if(i%prime[j]==0)
                    {
                        low[i*prime[j]]=low[i]*prime[j];
                        nu[i*prime[j]]=nu[i]+1;
                        if(i==low[i])
                        {
                            f[i*prime[j]]=qpow(nu[i*prime[j]]+1,p-2,p);
                        }
                        else
                        {
                            f[i*prime[j]]=f[i/low[i]]*f[low[i*prime[j]]]%p;
                        }
                    }
                    else
                    {
                        low[i*prime[j]]=prime[j];
                        nu[i*prime[j]]=prime[j];
                        f[i*prime[j]]=f[i]*f[prime[j]]%p;
                    }
                }
            }
        }
        int main()
        {
        #define Isaac
        #ifdef Isaac
            freopen("set.in","r",stdin);
            freopen("set.out","w",stdout);
        #endif
            ll n,ans=0,i;
            cin>>n>>p;
            isprime(n);
            for(i=1;i<=n;i++)
            {
                ans=(ans+f[i])%p;
            }
            cout<<ans<<endl;
            return 0;
        }
        
  • 正解

总结

  • T2 没想到还可以直接删掉 bj ,挂了 40pts
  • T3 赛时发现的链长为 2 的性质无法直接扩展到长度为 k3 的性质。
posted @   hzoi_Shadow  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
扩大
缩小
点击右上角即可分享
微信分享提示