题解 P2511 【[HAOI2008]木棍分割】

y2823774827y

2019-02-16 09:22:22

Solution

一眼题系列,本题似乎没有暴力分~~整个题就是暴力~~ $(1)$二分解决 $(2)$ 1. 二分预处理$let[i]$ 为能与第$i$个组成块的最左边位置,不过本题没卡$O(n^2)$也可过 $~~~~~~$2.$dp[i][j]$为第$i$个为第$j$块结尾的方案数: $~~~~~~~~dp[i][j]=\sum\limits_{k=let[i]}^idp[k-1][j-1]$显然空间时间都会爆 $~~~~~~~~$改成滚动数组,套个前缀和 取模和$long long$这里注意一下不开O2也能过~~本人太懒~~ ```cpp #include<bits/stdc++.h> using namespace std; typedef long long LL; const LL maxn=50009,p=10007; LL n,m,sum,ans,num,now,lst; LL a[maxn],let[maxn],dp[2][maxn],pre[2][maxn]; inline bool Check(LL x){ LL len(0),ret(1); for(LL i=1;i<=n;++i){ if(a[i]>x) return false; len+=a[i]; if(len>x) ++ret,len=a[i]; } return ret<=m?true:false; } int main(){ scanf("%lld%lld",&n,&m); ++m; for(LL i=1;i<=n;++i) scanf("%lld",a+i),sum+=a[i]; LL l(1),r(sum); while(l<=r){ LL mid(l+r>>1); if(Check(mid)) r=mid-1,ans=mid; else l=mid+1; } for(LL i=1;i<=n;++i){ LL len(0); for(LL j=i;j>=1;--j){ len+=a[j]; if(len>ans){ let[i]=j+1; break; } } if(!let[i]) let[i]=1; } now=0,lst=1; for(LL i=0;i<=n;++i) pre[0][i]=1; for(LL j=1;j<=m;++j){ now^=1,lst^=1; memset(pre[now],0,sizeof(pre[now])); memset(dp[now],0,sizeof(dp[now])); for(LL i=1;i<=n;++i){ if(i<j) dp[now][i]=0; else if(let[i]-2>=0) dp[now][i]=(pre[lst][i-1]-pre[lst][let[i]-2]+p)%p; else dp[now][i]=pre[lst][i-1]; pre[now][i]=(pre[now][i-1]+dp[now][i])%p; } num=(num+dp[now][n])%p; } printf("%lld %lld",ans,num); return 0; } ```