s很气的DP
/************************************************************** Problem: 1044 User: lxy8584099 Language: C++ Result: Accepted Time:3428 ms Memory:1800 kb ****************************************************************/ /* 先二分答案 然后DP i,j 表示前i个分成j块的方案数 因为要满足 suni-sunk<=len 所以可以利用sum的单调性 预处理出所有i对应的最小的k 处理更新一个dp的前缀和 就可以省去k的枚举 */ #include<cstdio> using namespace std; const int N=5e4+50; const int p=10007; int n,m,a[N],l,dp[N],sum[N],f[N],left[N]; bool check(int x) { int now,k; now=k=0; for(int i=1;i<=n;i++) { now+=a[i]; if(now>x) k++,now=a[i]; if(now>x) return 0; } return k<=m; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum[i]=sum[i-1]+a[i]; int L=1,R=sum[n],mid; while(L<=R) { mid=(L+R)>>1; if(check(mid)) R=mid-1,l=mid; else L=mid+1; } int k=0; for(int i=1;i<=n;i++) // 前缀和单调 所以可以继续利用上一次的 k 值 for(;;k++) if(sum[i]-sum[k]<=l) {left[i]=k;break;} for(int i=1;i<=n;i++) { dp[i]=(sum[i]<=l); f[i]=(f[i-1]+dp[i])%p; } int res=dp[n]; for(int j=2;j<=m+1;j++) { for(int i=1;i<=n;i++) { // dp[i]=f[i-1] - f[left[i]-1] 消去了 k循环 dp[i]=f[i-1]; if(left[i]-1>=0) dp[i]=((dp[i]-f[left[i]-1])%p+p)%p; } for(int i=1;i<=n;i++) f[i]=f[i-1]+dp[i]; // 更新 f res=(res+dp[n])%p; } printf("%d %d\n",l,res); return 0; }