题解:P10627 [JOI Open 2024] 中暑 / Heat Stroke
masterhuang · · 题解
给定
n 个盒子,第i 个盒子容量为C_i 。然后依次给出
m 个球,第i 个球可以放到X_i 或X_i + 1 号盒子,如果两个盒子都满了就丢弃,求最多可能丢弃多少个球。
这题 JOI 场上零个人通过!
本文参考补充了 DaiRuiChen007 的题解。
考虑经典套路:把盒子
则丢弃的个数
考虑一个
建立二分图,左部点是所有球,右部点是
然后
此时等价于一个完美匹配右部组的问题。考虑应用 Hall 定理。
Hall 定理啥时候只需要考虑区间?
二分图一侧顶点集
Y 可以被恰当排序,使得另一侧X 的每一个顶点x 的邻居集合N(x) 都是Y 中一段连续的区间时。
于是这题只需要考虑右部点的区间
- 特别的,对于
t_i=\infty 的位置,不需要判定经过它的区间。
考虑计算
记原来的
变化为
在 dp 的过程中记录后缀最小值:记
注意到
因为
相当于把这
s_i 个点离散下来,然后再 dp 转移。
转移时就枚举
于是写出一份 优美的
\mathcal{O}(m^3) 代码,由于这东西卡不满获得95 分的高分。不理解上面内容的建议严肃观看这个三方代码,对辅助理解起到了极大作用。
判掉
- 不过有一个貌似是斜线后缀
\max ,稍微注意一下即可。
时间复杂度
:::info[代码]
#include<bits/stdc++.h>
#define LL long long
#define sz(x) ((int)x.size())
#define all(x) x.begin(),x.end()
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int N=8005,inf=2e9;
int n,m,c[N],C[N],X[N],R[N],ans,d[N];
basic_string<int>G[N];
vector<vector<int>>f,F,g;
inline void cmx(int &x,int y){x=max(x,y);}
inline int W(int i,int x){return lower_bound(all(G[i]),x)-G[i].begin();}
int main()
{
cin.tie(0)->sync_with_stdio(0);cout.tie(0);cin>>n;
for(int i=1;i<=n;i++) cin>>C[i],G[i]={0};cin>>m;
for(int i=1;i<=m;i++) cin>>X[i],G[X[i]]+=i,G[X[i]+1]+=i;
for(int i=1;i<=n;i++) C[i]=min(C[i],(d[i]=sz(G[i]))-1),R[i]=d[i]-C[i];
f=vector<vector<int>>(R[1],vector<int>(R[1],-inf));
for(int J=0;J<R[1];J++) f[J][J]=0;
for(int i=2,V;i<=n;i++)
{
g=F=vector<vector<int>>(R[i],vector<int>(R[i],-inf));
for(int j=1;j<=m;j++) c[j]=c[j-1]+(X[j]==i-1);
if(R[i-1]>=1)
{
int j=d[i-1]-1,_j=j-C[i-1];
for(int k=0;k<R[i-1];k++) if((V=f[_j][k])>=0)
for(int J=0;J<R[i];J++) cmx(F[J][J],V+c[m]-c[max(G[i-1][j],G[i][J+C[i]])]);
}
if(R[i]>=1)
{
int J=R[i]-1;
for(int j=C[i-1];j<d[i-1]-1;j++) for(int k=0;k<R[i-1];k++) if((V=f[j-C[i-1]][k])>=0)
cmx(F[J][J],V+c[m]-c[max(G[i-1][j],G[i][J+C[i]])]);
}//两个 inf 的转移
for(int j=C[i-1];j<d[i-1]-1;j++)
{
int x=G[i-1][j],p=W(i,x);
for(int k=0;k<R[i-1];k++) if((V=f[j-C[i-1]][k])>=0)
{
int D=min(0,k-c[x]),l=max(p,C[i]-D)-C[i];
if(l+C[i]<d[i]-1) cmx(g[l][l+D],V);
}
}
for(int j=1;j<R[i]-1;j++) for(int k=1;k<R[i];k++) cmx(g[j][k],g[j-1][k-1]);//斜线 max
for(int j=0;j<R[i]-1;j++) for(int k=0;k<R[i];k++) cmx(F[j][k],g[j][k]+c[m]-c[G[i][j+C[i]]]);
g=vector<vector<int>>(R[i-1],vector<int>(R[i-1],-inf));
for(int j=R[i-1]-2;j>=0;j--) for(int k=0;k<R[i-1];k++)
g[j][k]=max(g[j+1][k],f[j][k]+c[m]-c[G[i-1][j+C[i-1]]]);//后缀 max
for(int J=C[i];J<d[i]-1;J++) for(int k=0;k<R[i-1];k++)
{
int K=min(0,k-c[G[i][J]])+J-C[i];
if(K<0) continue;
int p=max(C[i-1],W(i-1,G[i][J]+1))-C[i-1];
if(p<R[i-1]-1) cmx(F[J-C[i]][K],g[p][k]);
}
swap(f,F);
}
for(int i=0;i<R[n];i++) for(int x:f[i]) cmx(ans,x);
cout<<ans;
return 0;
}
:::