【题目链接】
http://www.lydsy.com/JudgeOnline/problem.php?id=1044
【题意】
n根木棍拼到一起,最多可以切m刀,问切成后最大段的最小值及其方案数。
【思路】
对于第一问可以二分后贪心判断。
假设第一问得到的答案为L,设f[i][j]前i个木棍切j下且保持段长不超过L的方案数,则有转移式:
f[i][j]=sigma { f[k][j-1] },k<i,suma(k+1,i)<=L
优化:
空间方面可以用个滚动数组。
时间方面由于前缀和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 ",ans); 81 return 0; 82 }