2025省选模拟9

2025省选模拟9

组题人: @Chen_jr | @Delov | @APJifengc

T1 HZTG3002. Delov 的 npy 们 25pts

  • 原题: [AGC031E] Snuke the Phantom Thief

  • 部分分

    • 25pts :爆搜。

      点击查看代码
      ll x[120],y[120],z[120],cnt[2][120],pre[2][120],suf[2][120],a[420],b[420],ans=0;
      char op[420];
      void dfs(ll pos,ll n,ll m,ll sum)
      {
          if(pos==n+1)
          {
              for(ll i=1;i<=100;i++)
              {
                  pre[0][i]=pre[0][i-1]+cnt[0][i];
                  pre[1][i]=pre[1][i-1]+cnt[1][i];
              }
              for(ll i=100;i>=1;i--)
              {
                  suf[0][i]=suf[0][i+1]+cnt[0][i];
                  suf[1][i]=suf[1][i+1]+cnt[1][i];
              }
              for(ll i=1;i<=m;i++)
              {
                  if(op[i]=='L'&&pre[0][a[i]]>b[i])  return;
                  if(op[i]=='R'&&suf[0][a[i]]>b[i])  return;
                  if(op[i]=='D'&&pre[1][a[i]]>b[i])  return;
                  if(op[i]=='U'&&suf[1][a[i]]>b[i])  return;
              }
              ans=max(ans,sum);
          }
          else
          {
              cnt[0][x[pos]]++;  cnt[1][y[pos]]++;
              dfs(pos+1,n,m,sum+z[pos]);
              cnt[0][x[pos]]--;  cnt[1][y[pos]]--;
              dfs(pos+1,n,m,sum);
          }
      }
      int main()
      {
      #define Isaac
      #ifdef Isaac
          freopen("a.in","r",stdin);
          freopen("a.out","w",stdout);
      #endif
          ll n,m,i;
          cin>>n;
          for(i=1;i<=n;i++)  cin>>x[i]>>y[i]>>z[i];
          cin>>m;
          for(i=1;i<=m;i++)  cin>>op[i]>>a[i]>>b[i];
          dfs(1,n,m,0);
          cout<<ans<<endl;
          return 0;
      }
      
  • 部分分

    • 四个方向分别处理网络流貌似无法处理。
    • 正难则反,考虑直接枚举选了多少个数,此时需要将限制条件转化为第 bi+1 的横/纵坐标 </>ai
    • 然后分别对横纵坐标建立左右部点,把费用挂在辅助点之间的连边上,只有横纵坐标都满足限制条件后才能取到费用。
    点击查看代码
    ll x[120],y[120],z[120],l[2][120],r[2][120],ans=0;
    struct MaxFlowMinCost
    {
        const ll inf=0x3f3f3f3f3f3f3f3f;
        struct node
        {
            ll nxt,to,w,cap,flow;
        }e[20010];
        ll head[510],vis[510],cur[510],dis[510],cnt=1,cost;
        void clear()
        {
            memset(e,0,sizeof(e));
            memset(head,0,sizeof(head));
            cnt=1;
        }
        void add(ll u,ll v,ll w,ll _w)
        {
            cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
            cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
        }
        bool spfa(ll s,ll t)
        {
            memset(vis,0,sizeof(vis));
            memset(dis,-0x3f,sizeof(dis));
            queue<ll>q;
            dis[s]=0;
            q.push(s);  vis[s]=1;
            while(q.empty()==0)
            {
                ll x=q.front();  q.pop();
                vis[x]=0;
                cur[x]=head[x];
                for(ll i=head[x];i!=0;i=e[i].nxt)
                {
                    if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow)
                    {
                        dis[e[i].to]=dis[x]+e[i].w;
                        if(vis[e[i].to]==0)
                        {
                            q.push(e[i].to);  vis[e[i].to]=1;
                        }
                    }
                }
            }
            return dis[t]>0;
        }
        ll dfs(ll x,ll t,ll flow)
        {
            if(x==t)  return flow;
            vis[x]=1;
            ll sum=0,tmp;
            for(ll i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
            {
                cur[x]=i;
                if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
                {
                    tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
                    sum+=tmp;
                    e[i].flow+=tmp;  e[i^1].flow-=tmp;
                    cost+=tmp*e[i].w;
                }
            }
            vis[x]=0;
            return sum;
        }
        ll Dinic(ll s,ll t)
        {
            cost=0;
            while(spfa(s,t)==true)
            {
                while(dfs(s,t,inf));
            }
            return cost;
        }
    }C;
    ll solve(ll n,ll m,ll mid)
    {
        C.clear();
        ll s=0,t=2*n+2*mid+1;
        for(ll i=1;i<=mid;i++)  
        {
            C.add(s,i,1,0);
            C.add(i+mid+2*n,t,1,0);
        }
        for(ll i=1;i<=n;i++)
        {
            C.add(i+mid,i+mid+n,1,z[i]);// 辅助点
            for(ll j=1;j<=mid;j++)
            {
                if(l[0][j]<=x[i]&&x[i]<=r[0][mid-j+1])  C.add(j,i+mid,1,0);//限制点和辅助点之间的连边
                if(l[1][j]<=y[i]&&y[i]<=r[1][mid-j+1])  C.add(i+mid+n,j+mid+2*n,1,0);
            }
        }
        return C.Dinic(s,t);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
    #endif
        ll n,m,a,b,ans=0,i,j;
        char pd;
        cin>>n;
        for(i=1;i<=n;i++)  cin>>x[i]>>y[i]>>z[i];
        cin>>m;
        memset(r,0x3f,sizeof(r));
        for(i=1;i<=m;i++)  
        {
            cin>>pd>>a>>b;  b++;
            if(pd=='L')  l[0][b]=max(l[0][b],a+1);
            if(pd=='R')  r[0][b]=min(r[0][b],a-1);
            if(pd=='D')	 l[1][b]=max(l[1][b],a+1);
            if(pd=='U')  r[1][b]=min(r[1][b],a-1);
        }
        for(i=1;i<=n;i++)
        {
            for(j=0;j<=1;j++)
            {
                l[j][i]=max(l[j][i-1],l[j][i]);  r[j][i]=min(r[j][i-1],r[j][i]);
            }
        }
        for(i=1;i<=n;i++)  ans=max(ans,solve(n,m,i));
        cout<<ans<<endl;
        return 0;
    }
    

T2 HZTG3003. 皮胚 0pts

  • 原题: CF1572D Bridge Club

  • 观察到最终连出的边是一个二分图的形式,考虑按照 popcount 奇偶性分成左、右部点然后进行优化带权二分图匹配。

  • 因为有 k 的限制,考虑最大费用流建源、汇点加一个中转点辅助转移。暴力费用流无法接受,需要进一步分析性质。

  • 每选出一条边,就会减少 2n1 种决策,即最后的 k 个答案一定在前 k(2n1) 大的数里选。

  • 对长度为 107 的数组 sort 有点悬,考虑借助 nth_element 的力量。

    点击查看代码
    struct node
    {
        int u,v,w;
        bool operator < (const node &another) const
        {
            return w>another.w;
        }
    }q[11000010];
    int a[1100010],b[1100010],cnt=0,tot=3;
    void add(int u,int v,int w)
    {
        cnt++;  q[cnt]=(node){u,v,w};
    }
    struct MaxFlowMinCost
    {
        struct node
        {
            int nxt,to,w,cap,flow;
        }e[64010];
        int head[16010],vis[16010],incf[16010],dis[16010],pre[16010],cnt=1;
        void add(int u,int v,int w,int _w)
        {
            cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
            cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
        }
        bool spfa(int s,int t)
        {
            memset(vis,0,sizeof(vis));
            memset(dis,-0x3f,sizeof(dis));
            queue<int>q;
            dis[s]=0;  incf[s]=0x3f3f3f3f;
            q.push(s);  vis[s]=1;
            while(q.empty()==0)
            {
                int x=q.front();  q.pop();
                vis[x]=0;
                for(int i=head[x];i!=0;i=e[i].nxt)
                {
                    if(dis[e[i].to]<dis[x]+e[i].w&&e[i].cap>e[i].flow)
                    {
                        dis[e[i].to]=dis[x]+e[i].w;
                        incf[e[i].to]=min(incf[x],e[i].cap-e[i].flow);
                        pre[e[i].to]=i;
                        if(vis[e[i].to]==0)
                        {
                            q.push(e[i].to);  vis[e[i].to]=1;
                        }
                    }
                }
            }
            return dis[t]>0;
        }
        int update(int s,int t)
        {
            for(int x=t;x!=s;x=e[pre[x]^1].to)
            {
                e[pre[x]].flow+=incf[t];  e[pre[x]^1].flow-=incf[t];
            }
            return incf[t]*dis[t];
        }
        int Edmonds_Krap(int s,int t)
        {
            int cost=0;
            while(spfa(s,t)==true)  cost+=update(s,t);
            return cost;
        }
    }C;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("pp.in","r",stdin);
        freopen("pp.out","w",stdout);
    #endif
        int n,m,k,i,j;
        scanf("%d%d",&n,&k);  m=(1<<n)-1;
        for(i=0;i<=m;i++)  scanf("%d",&a[i]);
        for(i=0;i<=m;i++)
        {
            if(__builtin_popcount(i)%2==0)
            {
                for(j=0;j<n;j++)  add(i,i^(1<<j),a[i]+a[i^(1<<j)]);
            }
        }
        C.add(1,2,k,0);
        k=min(k*(2*n-1),cnt);
        nth_element(q+1,q+1+k,q+1+cnt);
        for(i=1;i<=k;i++)
        {
            if(b[q[i].u]==0)
            {
                tot++;  b[q[i].u]=tot;
            }
            if(b[q[i].v]==0)
            {
                tot++;  b[q[i].v]=tot;
            }
            C.add(b[q[i].u],b[q[i].v],1,q[i].w);
        }
        for(i=0;i<=m;i++)
        {
            if(b[i]!=0)
            {
                if(__builtin_popcount(i)%2==0)  C.add(2,b[i],1,0);
                else  C.add(b[i],3,1,0);
            }
        }
        cout<<C.Edmonds_Krap(1,3)<<endl;
        return 0;
    }
    

T3 HZTG3004. 流(flow) 45pts

  • 原题: CF708D Incorrect Flow

  • 部分分

    • 子任务 1,3 :枚举流量,相邻两点之间简单模拟一下。

      点击查看代码
      ll s[110][110];
      vector<pair<ll,ll> >e[110][110];
      void solve(ll n,ll mid,ll &ans)
      {
          ll sum=0,r;
          for(ll i=1;i<=n-1;i++)
          {
              r=mid-s[i][i+1];
              if(r<0) // 原来的多
              {
                  for(ll j=0;j<e[i][i+1].size();j++)
                  {
                      if(e[i][i+1][j].first>e[i][i+1][j].second)// 钦定 c 合法,降 f->c
                      {
                          sum+=e[i][i+1][j].first-e[i][i+1][j].second;// 降到一半使得 r 合法后 c 可能仍不合法需要补 c->f
                          if(sum>=ans)  return;
                          if(r<0)  r+=min(-r,e[i][i+1][j].first-e[i][i+1][j].second);	
                      }
                  }
                  for(ll j=e[i][i+1].size()-1;j>=0&&r<0;j--)
                  {
                      sum+=min(-r,e[i][i+1][j].first);//尝试降 f->0
                      if(sum>=ans)  return;
                      r+=min(-r,e[i][i+1][j].first);		
                  }
                  //r 不可能有剩余
              }
              else  // 原来的少
              {
                  for(ll j=0;j<e[i][i+1].size();j++)// 钦定 f 合法,补 c->f
                  {
                      if(e[i][i+1][j].first>e[i][i+1][j].second)
                      {
                          sum+=e[i][i+1][j].first-e[i][i+1][j].second;
                          if(sum>=ans)  return;
                      }
                  }
                  for(ll j=e[i][i+1].size()-1;j>=0&&r>0;j--)
                  {
                      if(e[i][i+1][j].first<=e[i][i+1][j].second)// 补 f->c
                      {
                          sum+=min(r,e[i][i+1][j].second-e[i][i+1][j].first);
                          if(sum>=ans)  return;
                          r-=min(r,e[i][i+1][j].second-e[i][i+1][j].first);
                      }
                  }
                  sum+=2*r;//同时提升 c,f
                  if(sum>=ans)  return;
              }
          }
          ans=sum;
      }
      int main()
      {
      #define Isaac
      #ifdef Isaac
          freopen("flow.in","r",stdin);
          freopen("flow.out","w",stdout);
      #endif
          ll n,m,u,v,c,f,ans=0x7f7f7f7f7f7f7f7f,i;
          cin>>n>>m;
          for(i=1;i<=m;i++)
          {
              cin>>u>>v>>c>>f;
              s[u][v]+=f;
              e[u][v].push_back(make_pair(f,c));
          }
          for(i=0;i<=10000*m;i++)  solve(n,i,ans);
          cout<<ans<<endl;
          return 0;
      }
      
  • 正解

    • fc,f>c 的边分开考虑,统计加流和退流的贡献。
      • fc ,连边 {vu,c=f,w=1uv,c=cf,w=1uv,c=,w=2 ,分别对应减小 f ,增加 f 但仍在 c 范围以内,增加 f 但超过 c
      • f>c ,不管怎么样都至少有 fc 的代价先算上,然后连边 {vu,c=fc,w=0vu,c=c,w=1uv,c=,w=2 ,分别对应减小 f 但仍 c ,减小 f 但小于 c ,增大 f
    • 保留原图上的边但 cup=cdown=c 然后再跑一遍有源汇有上下界最小费用可行流即可。
    点击查看代码
    const int inf=0x3f3f3f3f;
    struct UpDownFlowMinCost
    {
        struct node
        {
            int nxt,to,w,cap,flow;
        }e[3010];
        int head[110],dis[110],vis[110],cur[110],f[110],cnt=1,cost=0;
        void add(int u,int v,int w,int _w)
        {
            cnt++;  e[cnt]=(node){head[u],v,_w,w,0};  head[u]=cnt;
            cnt++;  e[cnt]=(node){head[v],u,-_w,0,0};  head[v]=cnt;
        }
        void _add(int u,int v,int up,int down,int _w)
        {
            add(u,v,up-down,_w);  cost+=down*_w;
            f[u]-=down;  f[v]+=down;
        }
        bool spfa(int s,int t)
        {
            memset(dis,0x3f,sizeof(dis));
            memset(vis,0,sizeof(vis));
            queue<int>q;
            dis[s]=0;  cur[s]=head[s];
            q.push(s);  vis[s]=1;
            while(q.empty()==0)
            {
                int x=q.front();  q.pop();
                vis[x]=0;
                for(int i=head[x];i!=0;i=e[i].nxt)
                {
                    if(dis[e[i].to]>dis[x]+e[i].w&&e[i].cap>e[i].flow)
                    {
                        dis[e[i].to]=dis[x]+e[i].w;  cur[e[i].to]=head[e[i].to];
                        if(vis[e[i].to]==0)
                        {
                            q.push(e[i].to);  vis[e[i].to]=1;
                        }
                    }
                }
            }
            return dis[t]<inf;
        }
        int dfs(int x,int t,int flow)
        {
            if(x==t)  return flow;
            vis[x]=1;
            int sum=0,tmp;
            for(int i=cur[x];i!=0&&sum<flow;i=e[i].nxt)
            {
                cur[x]=i;
                if(vis[e[i].to]==0&&dis[e[i].to]==dis[x]+e[i].w&&e[i].cap>e[i].flow)
                {
                    tmp=dfs(e[i].to,t,min(e[i].cap-e[i].flow,flow-sum));
                    sum+=tmp;
                    e[i].flow+=tmp;  e[i^1].flow-=tmp;
                    cost+=tmp*e[i].w;
                }
            }
            vis[x]=0;
            return sum;
        }
        int Dinic(int s,int t)
        {
            while(spfa(s,t)==true)  while(dfs(s,t,inf));
            return cost;
        }
        int query(int n,int s,int t)
        {
            int _s=n+1,_t=n+2;
            _add(t,s,inf,0,0);
            for(int i=1;i<=n;i++)
            {
                if(f[i]>0)  add(_s,i,f[i],0);
                if(f[i]<0)  add(i,_t,-f[i],0);
            }
            return Dinic(_s,_t);
        }
    }F;
    int main()
    {
    #define Isaac
    #ifdef Isaac
        freopen("flow.in","r",stdin);
        freopen("flow.out","w",stdout);
    #endif
        int n,m,u,v,c,f,sum=0,i;
        cin>>n>>m;
        for(i=1;i<=m;i++)
        {
            cin>>u>>v>>c>>f;  F._add(u,v,f,f,0);
            if(f<=c)
            {
                F._add(v,u,f,0,1);   F._add(u,v,c-f,0,1);  F._add(u,v,inf,0,2);
            }
            else
            {
                sum+=f-c;
                F._add(v,u,f-c,0,0);  F._add(v,u,c,0,1);  F._add(u,v,inf,0,2);
            }
        }
        cout<<F.query(n,1,n)+sum<<endl;
        return 0;
    }
    

总结

  • 一场三道网络流题,罚坐。
  • T2 赛时转化后的题面需要 T1 的数据范围开到 n2×107,m1×106 ,根本做不了。
  • T3 以为网络不会有环,成功口胡了一个在 DAG 上背包转移的做法。

后记

  • 开场约 2h 后才想起来下发大样例,且大样例是直接从测试点搬来的。
  • T1 没有设置部分分;有 bin 的数据, huge 说要从 AtCoder 把原数据搬下来,但效率感人。
  • 题目背景是熟悉的 @Chen_jr@Delov 与 npy 的整活。
posted @   hzoi_Shadow  阅读(52)  评论(5编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
扩大
缩小
点击右上角即可分享
微信分享提示