luogu P2511 [HAOI2008]木棍分割
阿新 • • 發佈:2018-10-17
getc memset haoi2008 木棍分割 個數 bit name mod ref
傳送門
第一問是一道經典的二分,二分答案\(ans\),然後從前往後掃,判斷要分成幾段救星了
第二問設\(f_{i,j}\)表示前\(i\)個數分成\(j\)段,每段之和不超過第一問答案的方案,轉移就是從\(f_{k,j-1}(k<i,(a_{k+1}+...+a_i)\leq ans)\)轉移過來,這些\(k\)是連續的一段,並且這一段隨著dp過程整體右移,所以搞個變量記錄一下合法轉移之和,再動態維護救星了
#include<bits/stdc++.h> #define LL long long #define il inline #define re register #define db double #define eps (1e-5) using namespace std; const int mod=10007; il LL rd() { LL x=0,w=1;char ch=0; while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();} while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();} return x*w; } int n,m,a[50010],f[2][50010]; il bool check(int mid) { for(int i=1,k=1,su=0;i<=n;i++) { su+=a[i]; if(su>mid) su=a[i],++k; if(k>m) return false; } return true; } int main() { n=rd(),m=rd()+1; int l=0,r=0; for(int i=1;i<=n;i++) a[i]=rd(),l=max(l,a[i]),r+=a[i]; int ans=r; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d ",ans); int nw=1,la=0; for(int i=1,su=0;i<=n;i++) { su+=a[i]; if(su>ans) break; f[la][i]=1; } int a2=f[la][n]; for(int j=2;j<=m;j++) { memset(f[nw],0,sizeof(f[nw])); for(int i=1,p=1,su=0,tm=0;i<=n;i++) { su+=a[i],tm+=f[la][i-1]; while(su>ans) su-=a[p],tm-=f[la][p-1],++p; f[nw][i]=(tm=(tm%mod+mod)%mod); } a2=(a2+f[nw][n])%mod; nw^=1,la^=1; } printf("%d\n",a2); return 0; }
luogu P2511 [HAOI2008]木棍分割