【题目连接】php
http://www.lydsy.com/JudgeOnline/problem.php?id=1044ios
【题意】git
n根木棍拼到一块儿,最多能够切m刀,问切成后最大段的最小值及其方案数。数组
【思路】优化
对于第一问能够二分后贪心判断。spa
假设第一问获得的答案为L,设f[i][j]前i个木棍切j下且保持段长不超过L的方案数,则有转移式:指针
f[i][j]=sigma { f[k][j-1] },k<i,suma(k+1,i)<=Lcode
优化:blog
空间方面能够用个滚动数组。get
时间方面因为前缀和sum是递增的,因此咱们拿个指针qf维护一个滑动窗口,使得窗口知足suma<=L,tot顺便记一下和就出来了。
【代码】
1 #include<set> 2 #include<cmath> 3 #include<queue> 4 #include<vector> 5 #include<cstdio> 6 #include<cstring> 7 #include<iostream> 8 #include<algorithm> 9 #define trav(u,i) for(int i=front[u];i;i=e[i].nxt) 10 #define FOR(a,b,c) for(int a=(b);a<=(c);a++) 11 #define rep(a,b,c) for(int a=(b);a>=(c);a--) 12 using namespace std; 13 14 typedef long long ll; 15 const int N = 1e5+10; 16 const int mod = 1e4+7; 17 18 ll read() { 19 char c=getchar(); 20 ll f=1,x=0; 21 while(!isdigit(c)) { 22 if(c=='-') f=-1; c=getchar(); 23 } 24 while(isdigit(c)) 25 x=x*10+c-'0',c=getchar(); 26 return x*f; 27 } 28 29 int n,m,ans,a[N],sum[N],f[2][N],cur,q[N],qf,qr; 30 31 bool can(int M) 32 { 33 int cnt=0,tot=0; 34 FOR(i,1,n) { 35 if(tot+a[i]>M) { 36 if((++cnt)>m) return 0; 37 tot=0; 38 } 39 tot+=a[i]; 40 } 41 return 1; 42 } 43 44 int main() 45 { 46 // freopen("in.in","r",stdin); 47 // freopen("out.out","w",stdout); 48 n=read(),m=read(); 49 int L=0,R=0; 50 FOR(i,1,n) 51 { 52 a[i]=read(), 53 sum[i]=sum[i-1]+a[i]; 54 L=max(L,a[i]); 55 } 56 R=sum[n]; 57 while(L<R) 58 { 59 int M=L+R>>1; 60 if(can(M)) R=M; else L=M+1; 61 } 62 printf("%d ",L); 63 64 FOR(j,0,m) 65 { 66 cur^=1; 67 int tot=0,qf=1; 68 FOR(i,1,n) 69 { 70 if(!j) f[cur][i]=sum[i]<=L; 71 else { 72 while(qf<i && sum[i]-sum[qf]>L) 73 tot=(tot-f[cur^1][qf++]+mod)%mod; 74 f[cur][i]=tot; 75 } 76 tot=(tot+f[cur^1][i])%mod; 77 } 78 ans=(ans+f[cur][n])%mod; 79 } 80 printf("%d\n",ans); 81 return 0; 82 }