1 条题解
-
0
自动搬运
来自洛谷,原作者为

niiick
**搬运于
2025-08-24 21:38:29,当前版本为作者最后更新于2018-10-30 12:03:18,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
首先最大的一段长度最小显然能马上想到二分, 先二分出这个长度,再DP求解方案数
假设求解出的长度为, 设表示前个木棍,分成组的方案数(注意是分组,不是切下)
先初始化
其中为满足的最小的
这样的方程复杂度,
实测一个点都过不了我们发现每次找到第一个的后,由于单调性,对于后面的一定也满足条件。 所以可以用前缀和维护数组,即
那么方程变为
但是到这里我们还是发现狂T不止,原因出在我们对于相同的重复去找,这显然不必要
我们一开始先预处理对于每个,满足的第一个是什么
int k=0; for(int i=1;i<=n;++i) for(;k<i;++k) if(sum[i]-sum[k]<=x){ rem[i]=k; break;}再次注意这里由于sum的单调性,不用每次置0,
否则你还是狂T不止到这里就结束了吗,还没!
我们发现每次转移的时候都只用到, 显然造成了不必要的空间浪费, 可以直接滚掉和的第一维
//niiick #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> using namespace std; int read() { int x=0,f=1; char ss=getchar(); while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();} while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();} return x*f; } const int mod=10007; const int maxn=50010; int n,m,mx,ans; int a[maxn],sum[maxn]; int dp[maxn],S[maxn]; int rem[maxn]; int check(int x) { int tot=0,len=0; for(int i=1;i<=n;++i) { if(len+a[i]>x) tot++,len=a[i]; else len+=a[i]; if(tot>m) return 0; } return tot<=m; } int DP(int x) { int k=0; for(int i=1;i<=n;++i) for(;k<i;++k) if(sum[i]-sum[k]<=x){ rem[i]=k; break;} int res=(sum[n]<=x); for(int i=1;i<=n;++i) { if(sum[i]<=x) dp[i]=1; S[i]=(S[i-1]+dp[i])%mod; } for(int i=2;i<=m+1;++i) { for(int j=1;j<=n;++j) { dp[j]=S[j-1]; if(rem[j]-1>=0) dp[j]=((dp[j]-S[rem[j]-1])%mod+mod)%mod;//注意减法出现负数 } for(int j=1;j<=n;++j) S[j]=(S[j-1]+dp[j])%mod; res=(res+dp[n])%mod; } return res; } int main() { n=read();m=read(); for(int i=1;i<=n;++i) a[i]=read(),sum[i]=sum[i-1]+a[i],mx=max(mx,a[i]); int L=mx,R=sum[n],mid; while(L<R) { mid=L+R>>1; if(check(mid)) ans=mid,R=mid; else L=mid+1; } printf("%d %d",ans,DP(ans)); return 0; }最后还是想再吐槽一下这题数据范围完全不科学啊
的DP+的二分,大概出题人
感性觉得能过就这样了吧
- 1
信息
- ID
- 1550
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者