2025省选模拟9
2025省选模拟9
组题人: @Chen_jr | @Delov | @APJifengc
HZTG3002. Delov 的 npy 们
-
部分分
-
:爆搜。点击查看代码
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; }
-
-
部分分
- 四个方向分别处理网络流貌似无法处理。
- 正难则反,考虑直接枚举选了多少个数,此时需要将限制条件转化为第
的横/纵坐标 。 - 然后分别对横纵坐标建立左右部点,把费用挂在辅助点之间的连边上,只有横纵坐标都满足限制条件后才能取到费用。
点击查看代码
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; }
HZTG3003. 皮胚
-
观察到最终连出的边是一个二分图的形式,考虑按照
奇偶性分成左、右部点然后进行优化带权二分图匹配。 -
因为有
的限制,考虑最大费用流建源、汇点加一个中转点辅助转移。暴力费用流无法接受,需要进一步分析性质。 -
每选出一条边,就会减少
种决策,即最后的 个答案一定在前 大的数里选。 -
对长度为
的数组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; }
HZTG3004. 流(flow)
-
部分分
-
子任务
:枚举流量,相邻两点之间简单模拟一下。点击查看代码
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; }
-
-
正解
- 对
的边分开考虑,统计加流和退流的贡献。- 若
,连边 ,分别对应减小 ,增加 但仍在 范围以内,增加 但超过 。 - 若
,不管怎么样都至少有 的代价先算上,然后连边 ,分别对应减小 但仍 ,减小 但小于 ,增大 。
- 若
- 保留原图上的边但
然后再跑一遍有源汇有上下界最小费用可行流即可。
点击查看代码
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; }
- 对
总结
- 一场三道网络流题,罚坐。
赛时转化后的题面需要 的数据范围开到 ,根本做不了。 以为网络不会有环,成功口胡了一个在 上背包转移的做法。
后记
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18700128,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)