2016-06-20
第一问是个二分的经典入门题
第二问很容易发现一个DP f[i][j]前i个木棍分j次合法方案数,f[i][j]=f[k][j-1]+...+f[i-1][j-1]; 但这样时间复杂度是O(mn^2),空间复杂度是O(mn) 但我们发现对于相同的j随着i的增加,对应的k也增加,那我们可以根据这个单调性,用前缀和来做到转移复杂度为O(1),空间可以用滚动数组来解决,本来我偷懒用short int开数组,不用滚动数组。结果T了一半,早就听说数组大了会使程序变慢,今天我可算知道了,泪。。。。。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<queue> 7 #define M 100008 8 #define ll long long 9 #define Mo 10007 10 using namespace std; 11 ll read() 12 { 13 char ch=getchar(); 14 ll x=0,f=1; 15 for(;ch<'0'||ch>'9';ch=getchar()) 16 if(ch=='-') 17 f=-1; 18 for(;ch>='0'&&ch<='9';ch=getchar()) 19 x=x*10+ch-'0'; 20 return x*f; 21 } 22 short int f[50010][2]; 23 int n,m,a[50009],l,r,ans,b[50009],p=1,q; 24 bool pan(int x) 25 { 26 int q=0,sum=0; 27 for(int i=1;i<=n;i++) 28 { 29 q+=a[i]; 30 if(q>x) 31 { 32 sum++; 33 q=a[i]; 34 } 35 } 36 if(sum<=m) 37 return 1; 38 return 0; 39 } 40 int main() 41 { 42 n=read(); 43 m=read(); 44 for(int i=1;i<=n;i++) 45 { 46 a[i]=read(); 47 l=max(a[i],l); 48 r+=a[i]; 49 } 50 for(;l<=r;) 51 { 52 int mid=(l+r)>>1; 53 if(pan(mid)) 54 { 55 ans=mid; 56 r=mid-1; 57 } 58 else 59 l=mid+1; 60 } 61 printf("%d ",ans); 62 for(int i=1;i<=n;i++) 63 a[i]+=a[i-1]; 64 for(int i=1;i<=n;i++) 65 if(a[i]<=ans) 66 f[i][p]=1; 67 int an=f[n][p]; 68 for(int i=1;i<=n;i++) 69 b[i]=f[i][p]+b[i-1]; 70 for(int i=1;i<=m;i++) 71 { 72 int h=0; 73 for(int j=i+1;j<=n;j++) 74 { 75 for(;a[j]-a[h]>ans;h++); 76 if(h) 77 f[j][q]=(b[j-1]-b[h-1]+Mo)%Mo; 78 else 79 f[j][q]=b[j-1]%Mo; 80 } 81 for(int j=1;j<=n;j++) 82 b[j]=f[j][q]+b[j-1]; 83 swap(p,q); 84 an=(an+f[n][p])%Mo; 85 } 86 printf("%d ",an); 87 return