https://www.luogu.com.cn/problem/P2511
https://darkbzoj.tk/problem/1044
dp+前缀和优化
人傻常数大,最后一个点跑了0.8秒/jk
第一问,直接二分,然后贪心来判断是否可行就好,记求出的答案(最大长度最小值)为 (len)
我一菜鸡在这一问就调了好长时间
预处理出一个 (p_i),表示的是,能与 (i) 构成一个区间的最左端的一个点是什么
比如 (p_i=j),也就是 ([j,i]) 区间是符合要求的。([j-1,i]) 就不是了,也就是它比 (len) 大
这个也可以简单的用二分求出
然后开始dp,设 (f_{i,j}) 为前 (i) 个木棍,恰好分成 (j) 段(不是切 (j) 刀),有多少方案
至于为什么是恰好这位大佬讲的挺详细了
那么,(f_{i,j}) 由 (f_{k,j-1}) 推出,也就是考虑给已经分成 (j-1) 段的每个情况,再加上一些木棍,更具体就是:
[f_{i,j}=sum_{k=p_i-1}^{i-1} f_{k,j-1}
]
也就是从 (p_i-1) 开始,可以为之前的状态加上 ([p_i,i]) 的木棍,来推出当前状态,直到枚举上限,就是加区间 ([i,i]) 的木棍
发现这样转移是 (n^3) 的,但是由于是区间求和的方式来转移,所以用一下前缀和优化
维护一个 (sum)
每次把 (f_{i-1,j-1}) 加入 (sum) 中,然后从 (sum) 中把所有 (f_{x,j-1},x<p_i-1) 减去
所以当前的 (f_{i,j}) 就等于 (sum) 了,复杂度 (n^2)
前缀和优化写的不熟,处理起边界来真的烦
然后数组要用滚动数组,答案是 (sum_{i=1}^{len} f_{n,i})
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
#define mod 10007
int a[50005];
int n,m,len;
int f[50005][2];//f[i][j] 前 i 个,分成 j 段,并符合最大长度的限制,的方案数
int sum[50005],p[50005];
inline int check(int max){
for(reg int i=1;i<=n;i++)if(a[i]>max) return 0;
for(reg int num=0,now,i=1;i<=n;){
now=0;
while(now<=max&&i<=n) now+=a[i],i++;
if(now>max) i--;
if(++num>m) return 0;
}
return 1;
}
inline int get(){
reg int l=1,r=sum[n],mid,ret;
while(l<=r){
mid=(l+r)>>1;
if(check(mid)) r=mid-1,ret=mid;
else l=mid+1;
}
return ret;
}
inline int pre(int x){
reg int l=1,r=x,ret,mid;
while(l<=r){
mid=(l+r)>>1;
if(sum[x]-sum[mid-1]<=len) ret=mid,r=mid-1;
else l=mid+1;
}
return ret;
}
inline int dp(){
for(reg int i=1;i<=n;i++) p[i]=pre(i);
for(reg int i=1;i<=n;i++)if(sum[i]<=len) f[i][1]=1;
int ans=f[n][1];
for(reg int tail,sum,j=2;j<=m;j++){
tail=p[2]-1;sum=f[p[2]-1][(j-1)&1];
for(reg int i=2;i<=n;i++){
sum=(sum+f[i-1][(j-1)&1])%mod;
while(tail<p[i]-1) sum=(sum-f[tail++][(j-1)&1]+mod)%mod;
f[i][j&1]=sum;
}
ans=(ans+f[n][j&1])%mod;
}
return ans;
}
int main(){
n=read();m=read()+1;
for(reg int i=1;i<=n;i++) a[i]=read(),sum[i]=sum[i-1]+a[i];
len=get();
std::printf("%d ",len);
std::printf("%d",dp());
return 0;
}