Wannafly模拟赛5 A Split 暴力 二分加速
你有一个大小为𝑆的𝐽𝑎𝑏𝑏𝑦,每次你可以从你已有的𝐽𝑎𝑏𝑏𝑦中选择一个大小不为1的𝐽𝑎𝑏𝑏𝑦,设他的大小为𝑄,然后把它分裂成𝑎和𝑄−𝑎,其中1≤𝑎<𝑄,这样你获得的收益是𝑎∗(𝑄−𝑎)给定𝑆,𝑀,求最少分裂几次才能得到至少𝑀的收益
思路:
首先需要证明
假设已经将题目中的S分成了n等份,那么对于当前分裂方式能够取得的最大收益需要满足:
当前假设S/n余数为0,或者尽可能接近均分
举例:
eg1. S=10 M=33
第一种方式:
所能够获得总收益为 come=5*5+2*3=31<33,若再将另外一个5分解才能满足>=33,此时分裂次数为3次
第二种方式:
此时总收益为 come=4*6+3*3=33 符合要求,分裂次数为2
对于第一种方式 (5,5)对于10是最优解,(2,3)对于5是最优解,但(2,3,5)对于10不是最优解
对于第二种方式(3,3)对于6是最优解,(4,6)对于10是最优解(再无法均分的情况下),同时(3,43,4)对于10是最优解,均分方式
eg2.假设最后分成了4份,如下:
则有:
总收益为:
则需保证(a1,a2)对于S1最优,(a3,a4)对于S2最优
那么(a1,a3),(a1,a4),(a2,a3),(a2,a4)也要保证最优,必有a1=a2=a3=a4=S/4(尽量平均分)
推论:对于总收益总能够表示为
发现 a1与其余每一项都有乘积形式
a2与其余每一项都有乘积形式
...
an与其余每一项都有乘积形式
这就要求尽可能将S均分给ai
解题思路:
枚举能够分成的份数从2到S,计算当前份数下能够获取的最大收益值与M比较,大于等于就跳出
暴力代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 int main() { 4 int s,m; 5 scanf("%d %d",&s,&m); 6 for(int i=2;i<=s;++i) { 7 long long come=0;//总收益 8 long long sum=0;//由下往上的数字和 9 int ai=s/i; 10 int r=s%i; 11 if(r>=2) { 12 come=(ai+1)*(ai+1); 13 sum=(ai+1)*2; 14 } else if(r==1) { 15 come=(ai+1)*ai; 16 sum=ai*2+1; 17 } else { 18 come=ai*ai; 19 sum=ai*2; 20 } 21 int num; 22 for(int j=3;j<=i;++j) { 23 if(j<=r) num=ai+1; 24 else num=ai; 25 come+=num*sum; 26 sum+=num; 27 } 28 if(come>=m) { 29 printf("%d\n",i-1); 30 return 0; 31 } 32 } 33 printf("-1\n"); 34 return 0; 35 }
二分加速代码:二分分割次数,速度更快
1 #include <bits/stdc++.h> 2 using namespace std; 3 long long check(int s,int k) 4 { 5 k++;//分割成k++份 6 int num=s/k;//每份的大小 7 int r=s%k;//多出来的部分 8 long long ans=0;//当前划分最大收益 9 for(int i=1;i<=r;++i){ 10 ans+=(num+1)*(s-num-1); 11 s-=num+1; 12 } 13 for(int i=r+1;i<k;++i) 14 { 15 ans+=num*(s-num); 16 s-=num; 17 } 18 return ans; 19 } 20 21 int main(){ 22 int s,m; 23 scanf("%d %d",&s,&m); 24 int l=1,r=s-1; 25 int k=-1; 26 while(l<=r){ 27 int mid=(l+r)>>1; 28 if(check(s,mid)>=m){ 29 r=mid-1; 30 k=mid; 31 }else l=mid+1; 32 } 33 printf("%d\n",k); 34 return 0; 35 }