bzoj1044题解

【题意分析】

  本题等价于如下描述:

  有一个长度为n的正整数序列,要求将其分解成m+1个子串,使最大子串和最小。求这个最大子串和及对应的分解方案数。

【解题思路】

  第一问二分+贪心即可。容易证明对于确定的最大子串和,分解子串使子串个数最小是一个具有最优子结构的问题。复杂度O(nlog2Σli)。

  第二问DP即可。先预处理前缀和Si=∑lj(j∈[1,i])。

  然后考虑状态:f[i][j]表示前i个元素分解成j个子串的合法方案数。

  易得转移方程:f[i][j]=Σf[k][j-1](k∈[j-1,i)且Si-Sk≤ans)

  但直接DP会TLE(时间复杂度O(mn2))+MLE(空间复杂度O(mn)),于是考虑优化:

•空间复杂度:因为DP向量f[][j]只跟f[][j-1]有关,可以用滚动数组优化,空间复杂度O(n);

•时间复杂度:固定j时,随着i的递增,可行的最小k是单调不减的,所以可以用单调队列优化,时间复杂度O(mn);

  最后答案即为∑f[n][j](j∈[1,m+1])。总复杂度O(n(m+log2Σli))。

【参考程序】

 1 #include <bits/stdc++.h>
 2 #define range(i,c,o) for(register int i=(c);i<(o);++i)
 3 #define dange(i,c,o) for(register int i=(c);i>(o);--i)
 4  
 5 #define __debug
 6 #ifdef __debug
 7     #define Function(type) type
 8     #define Procedure      void
 9 #else
10     #define Function(type) __attribute__((optimize("-O2"))) inline type
11     #define Procedure      __attribute__((optimize("-O2"))) inline void
12 #endif
13  
14 using namespace std;
15  
16 static const int AwD=10007;
17 Function(int&) inc(int&x,const int&y)
18 {
19     return (x+=y)>=AwD?x-=AwD:x;
20 }
21 Function(int&) dec(int&x,const int&y)
22 {
23     return (x-=y)<  0 ?x+=AwD:x;
24 }
25  
26 static int n,m;
27 int L[50005],S[50005]={0},las[50005],f[50005];
28  
29 int main()
30 {
31     scanf("%d%d",&n,&m); int l=0,r=0;
32     range(i,1,n+1) scanf("%d",L+i),l=max(l,L[i]),r+=L[i];
33     while(l<r)
34     {
35         int mid=l+r>>1,cnt=0,sum=0;
36         range(i,1,n+1) if((sum+=L[i])>mid) sum=L[i],++cnt;
37         cnt>m?l=mid+1:r=mid;
38     }
39     printf("%d ",r);
40     range(i,1,n+1) if((S[i]=S[i-1]+L[i])<=r) las[i]=1;
41     int ans=las[n];
42     range(i,2,m+2)
43     {
44         int h=1,sum=0; range(j,1,i) sum+=las[j],f[j]=0;
45         range(j,i,n+1)
46         {
47             for(;S[j]-S[h]>r;dec(sum,las[h++]));
48             f[j]=sum,inc(sum,las[j]);
49         }
50         memcpy(las,f,sizeof las),inc(ans,f[n]);
51     }
52     return printf("%d
",ans),0;
53 }
View Code
原文地址:https://www.cnblogs.com/spactim/p/6663567.html