目录
P1873 [COCI 2011/2012 #5] EKO / 砍树
P1678 烦恼的高考志愿——方法2:lower_bound
P1678 烦恼的高考志愿——方法3:upper_bound
P2249 【深基13.例1】查找
//找最先出现的
#include <bits/stdc++.h>
using namespace std;
int n, m, a[1000010], q, l, r, mid;
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
}
while(m--){
scanf("%d", &q);
l=1;
r=n;
while(l<r){
mid=(l+r)/2;
if(a[mid]>=q){ //找到靠前的
r=mid;
}
else{ //a[mid]<q
l=mid+1;
}
}
if(a[l]==q){
printf("%d ", l);
}
else{
printf("%d ", -1);
}
}
return 0;
}
//找最后出现的
#include <bits/stdc++.h>
using namespace std;
int n, m, a[1000010], q, l, r, mid;
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
}
while(m--){
scanf("%d", &q);
l=1;
r=n;
while(l<r){
//当l+1=r时, 如果mid=(l+r)/2=l, 而不是mid=(l+r+1)/2=r
//如果进入if分支, 会出现l=mid=l, 产生死循环
mid=(l+r+1)/2;
if(a[mid]<=q){ //找到靠后的
l=mid;
}
else{ //a[mid]>q
r=mid-1;
}
}
if(a[l]==q){
printf("%d\n", l);
}
else{
printf("%d\n", -1);
}
}
return 0;
}
//lower_bound
#include <bits/stdc++.h>
using namespace std;
int n, m, a[1000010], q, id;
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
}
while(m--){
scanf("%d", &q);
//找到第一个大于等于q的
id=lower_bound(a+1, a+n+1, q)-a;
if(a[id]==q){
printf("%d ", id);
}
else{
printf("-1 ");
}
}
return 0;
}
P1102 A-B 数对
//map
#include <bits/stdc++.h>
using namespace std;
int n, c;
int a[200000];
long long result;
map<int, int> m;
int main()
{
cin >> n >> c;
for(int i=0; i<n; i++){
cin >> a[i];
m[a[i]]++;
a[i]-=c; //a[i]+=c;也行
}
for(int i=0; i<n; i++){
result+=m[a[i]];
}
cout << result;
return 0;
}
//二分查找
#include<bits/stdc++.h>
using namespace std;
int a[200005],b[200005];
long long num[200005], res=0;
int n,c,cnt=1;
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>a[i];
}
sort(a+1,a+n+1);
num[1] = 1; b[1] = a[1];
for(int i=2;i<=n;i++){
if(a[i]!=a[i-1]){
b[++cnt]=a[i];
num[cnt]=1;
}
else num[cnt]++;
}
for(int i=1;i<=cnt;i++){
int l=1, r=cnt;
int x = c + b[i];
while(l<r){
int mid=(l+r)/2;
if(x <= b[mid]) r = mid;
else l = mid+1;
}
if(b[l] == x) res += num[i] * num[l];
}
cout<<res<<endl;
return 0;
}
P1824 进击的奶牛
#include <bits/stdc++.h>
using namespace std;
int n, a[100010], asd[100010], c, l=1e9, r=1e9, mid;
//检验所有牛的间距是否都大于等于x
//转换为依次保证牛的间距大于等于x, 看能否放下c头牛
bool check(int x)
{
int num=0; //满足间距大于等于x时, 能放的牛的数量
int temp=0; //当前牛棚距离前一个放牛的牛棚的间距
//枚举牛棚
for(int i=1; i<=n; ++i){
temp+=asd[i]; //加上第i个牛棚和第i-1个牛棚的间距
if(temp>=x){ //如果大于等于x了, 说明第i个牛棚可以放牛
temp=0; //间距归零
num++; //能放的牛数++
if(num>=c){
return true; //减少一些循环次数
}
}
}
return false;
}
int main()
{
scanf("%d %d", &n, &c);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]); //输入每个牛棚的坐标
}
sort(a+1, a+n+1); //排序, 题目没说有序
for(int i=2; i<=n; ++i){
asd[i]=a[i]-a[i-1]; //第i个牛棚距离前一个牛棚的间距
l=min(l, asd[i]); //最近距离的左边界
}
asd[1]=1e9;
while(l<r){
mid=(l+r+1)/2;
if(check(mid)){ //可以满足所有牛(c头)的距离都大于等于mid, 说明最近距离可能更大
l=mid; //右半部分找答案
}
else{ //无法满足所有牛(c头)的距离都大于等于mid, 说明最近距离达不到mid
r=mid-1; //左半部分找答案
}
}
printf("%d", l);
return 0;
}
P1577 切绳子
100分做法,避免精度问题,将原属于扩大100倍,巧妙处理舍掉小数
#include <bits/stdc++.h>
using namespace std;
int n, k;
double a[100010], l, r, mid, asd;
//判断切割后每条绳子的长度为ans时, 能否切割出k条绳子
bool check(double ans)
{
int total=0;
for(int i=1; i<=n; ++i){
total+=a[i]/ans; //第i根绳子的长度除以切割的长度
if(total>=k){ //如果已经够k条绳子, 直接返回true
return true; //能切割出k条绳子
}
}
return false; //切割不出k条绳子
}
int main()
{
scanf("%d %d", &n, &k);
//给定的n条绳子长度
for(int i=1; i<=n; ++i){
scanf("%lf", &a[i]);
a[i]*=100;
r=max(r, a[i]); //初始化二分查找右边界
}
//初始化二分查找左边界
l=0;
//二分查找
while(l<r){
mid=(l+r+1)/2;
if(check(mid)){ //可以割出k条长度为mid的绳子
l=mid;
}
else{ //割不出k条长度为mid的绳子
r=mid-1;
}
}
printf("%lf", l/100); //注意,不是保留两位小数
return 0;
}
77分做法,没直接舍掉后面的小数
#include <bits/stdc++.h>
using namespace std;
int n, k;
double a[100010], l, r, mid;
//判断切割后每条绳子的长度为ans时, 能否切割出k条绳子
bool check(double ans)
{
int total=0;
for(int i=1; i<=n; ++i){
total+=int(a[i]/ans); //第i根绳子的长度除以切割的长度
if(total>=k){ //如果已经够k条绳子, 直接返回true
return true; //能切割出k条绳子
}
}
return false; //切割不出k条绳子
}
int main()
{
scanf("%d %d", &n, &k);
//给定的n条绳子长度
for(int i=1; i<=n; ++i){
scanf("%lf", &a[i]);
}
sort(a+1, a+n+1); //对给定的n条绳子从小到大排序
//初始化二分查找左边界, 注意不能为0
l=0.001;
//初始化二分查找右边界
r=a[n];
//二分查找
while(l+0.001<r){
mid=(l+r)/2;
if(check(mid)){ //可以割出k条长度为mid的绳子
l=mid;
}
else{ //割不出k条长度为mid的绳子
r=mid-0.001;
}
}
printf("%.2lf", l); //题目要求舍掉, 这样会四舍五入
return 0;
}
93分做法,double精度问题
#include <bits/stdc++.h>
using namespace std;
int n, k;
double a[100010], l, r, mid;
//判断切割后每条绳子的长度为ans时, 能否切割出k条绳子
bool check(double ans)
{
int total=0;
for(int i=1; i<=n; ++i){
total+=int(a[i]/ans); //第i根绳子的长度除以切割的长度
if(total>=k){ //如果已经够k条绳子, 直接返回true
return true; //能切割出k条绳子
}
}
return false; //切割不出k条绳子
}
int main()
{
scanf("%d %d", &n, &k);
//给定的n条绳子长度
for(int i=1; i<=n; ++i){
scanf("%lf", &a[i]);
}
sort(a+1, a+n+1); //对给定的n条绳子从小到大排序
//初始化二分查找左边界, 注意不能为0
l=0.001;
//初始化二分查找右边界
r=a[n];
//二分查找
while(l+0.001<r){
mid=(l+r)/2;
if(check(mid)){ //可以割出k条长度为mid的绳子
l=mid;
}
else{ //割不出k条长度为mid的绳子
r=mid-0.001;
}
}
//四舍五入77分
// printf("%.2lf", l);
// printf("%.2lf\n", 2.238); //输出2.24
// printf("%.2lf", 2.234); //输出2.23
printf("%d.", int(l));
l=l-int(l);
l*=10;
printf("%d", int(l));
l=l-int(l);
l*=10;
printf("%d", int(l));
return 0;
}
100分做法,输出r,但其实不应该
#include <bits/stdc++.h>
using namespace std;
int n, k;
double a[100010], l, r, mid;
//判断切割后每条绳子的长度为ans时, 能否切割出k条绳子
bool check(double ans)
{
int total=0;
for(int i=1; i<=n; ++i){
total+=int(a[i]/ans); //第i根绳子的长度除以切割的长度
if(total>=k){ //如果已经够k条绳子, 直接返回true
return true; //能切割出k条绳子
}
}
return false; //切割不出k条绳子
}
int main()
{
scanf("%d %d", &n, &k);
//给定的n条绳子长度
for(int i=1; i<=n; ++i){
scanf("%lf", &a[i]);
}
sort(a+1, a+n+1); //对给定的n条绳子从小到大排序
//初始化二分查找左边界, 注意不能为0
l=0.001;
//初始化二分查找右边界
r=a[n];
//二分查找
while(l+0.001<r){
mid=(l+r)/2;
if(check(mid)){ //可以割出k条长度为mid的绳子
l=mid;
}
else{ //割不出k条长度为mid的绳子
r=mid-0.001;
}
}
//四舍五入77分
// printf("%.2lf", l);
// printf("%.2lf\n", 2.238); //输出2.24
// printf("%.2lf", 2.234); //输出2.23
l=r;
printf("%d.", int(l));
l=l-int(l);
l*=10;
printf("%d", int(l));
l=l-int(l);
l*=10;
printf("%d", int(l));
return 0;
}
P1873 [COCI 2011/2012 #5] EKO / 砍树
#include <bits/stdc++.h>
using namespace std;
int n, m, h, a[1000010];
int l, r, mid;
//检验锯片高度为hight时, 能否得到M米的木材
//时间复杂度O(n), 10^6
bool check(int hight)
{
long long sum=0;
for(int i=1; i<=n; ++i){
if(a[i]>hight){
sum+=a[i]-hight;
}
}
if(sum>=m){
return true;
}
else{
return false;
}
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
//锯片的最大高度
r=max(r, a[i]);
}
//锯片的最小高度
l=0;
while(l<r){
mid=(l+r+1)/2;
//锯片高度为mid时,可以锯出m的木材
if(check(mid)){
//尝试升高锯片高度
l=mid;
}
else if(check(mid)==false){
//锯片高度为mid时,无法锯出m的木材
//只能降低锯片高度
r=mid-1;
}
}
printf("%d", r);
return 0;
}
P2440 木材加工
#include <bits/stdc++.h>
using namespace std;
int n, k, a[100010];
int lef, righ, mid;
long long sum;
//检验锯片高度为hight时, 能否得到M米的木材
//时间复杂度O(n), 10^6
bool check(int l)
{
long long total=0;
for(int i=1; i<=n; ++i){
total+=a[i]/l;
if(total>=k){
return true;
}
}
return false;
}
int main()
{
scanf("%d %d", &n, &k);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
sum+=a[i];
//righ表示切出小段可能的最大值
righ=max(righ, a[i]);
}
//要得到小段的数量大于总长度, 则无法切出
if(k>sum){
printf("0");
return 0;
}
lef=1;
while(lef<righ){
mid=(lef+righ+1)/2;
//小段为mid时, 能够切出k根
if(check(mid)){
//尝试增加小段的长度
lef=mid;
}
else if(check(mid)==false){
//小段为mid时, 无法切出k根及以上
//只能减少小段的长度
righ=mid-1;
}
}
printf("%d", righ);
return 0;
}
P1678 烦恼的高考志愿——方法1:手写二分
#include <bits/stdc++.h>
using namespace std;
int m, a[100010], n, b[100010], l, r, mid, x;
long long ans;
bool flag;
int main()
{
//学校数、学生数
scanf("%d %d", &m, &n);
//m个学校的分数线
for(int i=1; i<=m; ++i){
scanf("%d", &a[i]);
}
//n个学生的估分
for(int i=1; i<=n; ++i){
scanf("%d", &b[i]);
}
//对学校按分数线从低到高排序
sort(a+1, a+m+1);
//枚举每个学生的估分
for(int i=1; i<=n; ++i){
x=b[i];
if(x<=a[1]){
ans+=a[1]-x;
continue;
}
else if(x>=a[m]){
ans+=x-a[m];
continue;
}
//flag如果为true, 表示恰好有学校录取线等于估分
flag=false;
//二分查找第一所录取线大于等于该分数的学校
l=1;
r=m;
while(l<r){
mid=(l+r)/2;
if(a[mid]==x){
flag=true;
break;
}
else if(a[mid]>x){ //最终找到的可能是mid
r=mid;
}
else{ //最终找到的不可能是mid
l=mid+1;
}
}
if(flag){
continue;
}
//比较哪个学校录取线和该同学的估分更接近
if(abs(a[l-1]-x)<abs(a[l]-x)){
ans+=abs(a[l-1]-x);
}
else{
ans+=abs(a[l]-x);
}
}
printf("%lld", ans);
return 0;
}
P1678 烦恼的高考志愿——方法2:lower_bound
#include <bits/stdc++.h>
using namespace std;
int m, a[100010], n, b[100010], id, x;
long long ans;
bool flag;
int main()
{
//学校数、学生数
scanf("%d %d", &m, &n);
//m个学校的分数线
for(int i=1; i<=m; ++i){
scanf("%d", &a[i]);
}
//n个学生的估分
for(int i=1; i<=n; ++i){
scanf("%d", &b[i]);
}
//对学校按分数线从低到高排序
sort(a+1, a+m+1);
//枚举每个学生的估分
for(int i=1; i<=n; ++i){
x=b[i];
if(x<=a[1]){
ans+=a[1]-x;
continue;
}
else if(x>=a[m]){
ans+=x-a[m];
continue;
}
//在a数组中找到第一个大于等于x的下标
id=lower_bound(a+1, a+m+1, x)-a;
//id为第一个大于等于x的位置, 则id-1为第一个小于x的位置
//比较哪个学校录取线和该同学的估分更接近
if(abs(a[id-1]-x)<abs(a[id]-x)){
ans+=abs(a[id-1]-x);
}
else{
ans+=abs(a[id]-x);
}
}
printf("%lld", ans);
return 0;
}
P1678 烦恼的高考志愿——方法3:upper_bound
#include <bits/stdc++.h>
using namespace std;
int m, a[100010], n, b[100010], id, x;
long long ans;
bool flag;
int main()
{
//学校数、学生数
scanf("%d %d", &m, &n);
//m个学校的分数线
for(int i=1; i<=m; ++i){
scanf("%d", &a[i]);
}
//n个学生的估分
for(int i=1; i<=n; ++i){
scanf("%d", &b[i]);
}
//对学校按分数线从低到高排序
sort(a+1, a+m+1);
//枚举每个学生的估分
for(int i=1; i<=n; ++i){
x=b[i];
if(x<=a[1]){
ans+=a[1]-x;
continue;
}
else if(x>=a[m]){
ans+=x-a[m];
continue;
}
//在a数组中找到第一个大于x的下标
id=upper_bound(a+1, a+m+1, x)-a;
//id为第一个大于x的位置, 则id-1为第一个小于等于x的位置
//比较哪个学校录取线和该同学的估分更接近
if(abs(a[id-1]-x)<abs(a[id]-x)){
ans+=abs(a[id-1]-x);
}
else{
ans+=abs(a[id]-x);
}
}
printf("%lld", ans);
return 0;
}
P1163 银行贷款——方法1:暴力枚举
#include <bits/stdc++.h>
using namespace std;
//贷款总金额、每月还款金额、分期付款还清需要的月数
double total, permonth, n;
double ans, temp1, temp2;
int main()
{
scanf("%lf %lf %lf", &total, &permonth, &n);
for(double ans=0; ans<=6; ans+=0.0001){
//ans月利率
//计算月利率为当前ans时, n个月结束后未还金额
temp1=total;
for(int i=1; i<=n; ++i){
temp1=temp1*(1+ans);
temp1-=permonth;
}
//计算月利率为当前ans+0.0001时, n个月结束后未还金额
temp2=total;
for(int i=1; i<=n; ++i){
temp2=temp2*(1+ans+0.0001);
temp2-=permonth;
}
//如果月利率为ans时未还金额小于等于0, 月利率为ans+0.0001时未还金额大于等于0
//找到答案了
if(temp1<=0 && temp2>=0){
printf("%.1lf", ans*100); //答案为ans, 因为ans一定能还完, ans+0.0001可能还不完
break;
}
}
return 0;
}
P1163 银行贷款——方法2:二分答案
#include <bits/stdc++.h>
using namespace std;
//贷款总金额、每月还款金额、分期付款还清需要的月数
double total, permonth, n;
double l, r, mid, temp;
int main()
{
scanf("%lf %lf %lf", &total, &permonth, &n);
l=0;
r=6;
while(l+0.0001<r){
mid=(l+r)/2;
//mid月利率
//计算月利率为当前mid时, n个月结束后未还金额
temp=total;
for(int i=1; i<=n; ++i){
temp=temp*(1+mid);
temp-=permonth;
}
//能还完, 利率低了
if(temp<0){
l=mid;
}
else if(temp>=0){ //还不完, 利率高了
r=mid-0.0001;
}
else if(temp==0){
break;
}
}
printf("%.1lf", l*100); //l能还完
return 0;
}
附3组测试数据
P1163_3.in
1 1 1
P1163_3.out
0.0
P1163_4.in
1000000000 2147483647 123
P1163_4.out
214.7
P1163_5.in
443423 23477 3767
P1163_5.out
5.3
P2678 [NOIP2015 提高组] 跳石头
#include <bits/stdc++.h>
using namespace std;
int dis, n, m, d[50010], l=1e9, r, mid, a[50010];
//判断最短跳跃距离为x时, 能否满足条件(至多移走m块岩石)
bool check(int x)
{
int asd=0, before=0;
for(int i=1; i<=n+1; ++i){
if(a[i]+before<x){ //从第i-1块石头跳到第i块石头的距离小于最短跳跃距离x了, 需要移走石头
asd++;
before+=a[i];
}
else{
before=0;
}
if(asd>m){ //如果需要移走的岩石数大于m, 返回false
return false;
}
}
return true;
}
int main()
{
scanf("%d %d %d", &dis, &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &d[i]); //d[i]表示第i块石头到起点的距离
a[i]=d[i]-d[i-1]; //a[i]表示从第i-1块石头跳到第i块石头的距离
l=min(l, a[i]); //找左边界的
}
a[n+1]=dis-d[n];
l=min(l, a[n+1]);
r=dis; //初始化右边界
while(l<r){
mid=(l+r+1)/2; //最短跳跃距离的最大值为mid
if(check(mid)){ //最短跳跃距离为mid时, 需要移走的岩石不超过m
l=mid;
}
else{ //最短跳跃距离为mid时, 需要移走的岩石超过m
r=mid-1; //mid不可能是一个答案
}
}
printf("%d", l);
return 0;
}
P1182 数列分段 Section II
#include <bits/stdc++.h>
using namespace std;
int n, m, l, r, mid, a[100010];
//判断每段和的最大值为x时, 能否将数列分成m段
bool check(int x)
{
int cursum=0, curnum=0; //当前这一段的和, 总共有多少段
for(int i=1; i<=n; ++i){
if(cursum+a[i]<x){ //将a[i]扩进来后当前段的和小于x, 有望将下一个整数也扩到这一段中
cursum+=a[i];
}
else if(cursum+a[i]>x){ //将a[i]扩进来后当前段的和大于x了, 这一段不能把a[i]扩进来
curnum++; //段数++
cursum=a[i]; //a[i]必须重开一段
if(cursum>x){ //如果a[i]大于每段和的最大值x, 与x为每段和的最大值矛盾。
return false;
}
}
else if(cursum+a[i]==x){ //如果将a[i] 扩进来恰好够x, 则这一段刚好
curnum++; //段数++
cursum=0; //下一段重新开始
}
}
//处理最后一段
if(cursum>0){ //如果最后一段不为0
curnum++; //成一段
}
//能合在一段就合在一段的情况下, 得到的段数curnum小于等于m, 则一定能拆成m段。
return curnum<=m;
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]);
l=max(l, a[i]); //左边界, 每段和的最大值的最小, 至少要大于等于每个整数, 即a[i]
}
r=1e9; //右边界
while(l<r){
mid=(l+r)/2; //每段最大和为mid
if(check(mid)){ //如果每段最大和都不超过mid, 恰好能分成m段
r=mid; //题目要求的是每段和的最大值的最小, 答案在mid左半部分, 且包含mid
}
else{ //每段最大和不超过mid, 无法分成m段。或者有的段已经超过mid了
l=mid+1; //每段的最大和为mid时无法分成m段, 说明答案在mid右侧, 且不包含mid
}
}
printf("%d", l);
return 0;
}
附1组测试数据
P1182_1.in
6 3
4 2 4 5 1 1
P1182_1.out
7
P3853 [TJOI2007]路标设置
#include <bits/stdc++.h>
using namespace std;
int dis, n, k; //dis公路的长度、n原有路标的数量、最多可增设的路标数量
int a[100010], asd[100010];
int l, r, mid, temp;
//相邻路标的最大距离定义为该公路的 空旷指数
//求:增设路标后能达到的最小 空旷指数 值
//判断为达到空旷指数为x, 即相邻路标距离不能超过x, 需要增设的路标数量是否超过k
//如果没超过k, 则可以, 如果超过了k, 则不可以, 返回false
bool check(int x){
int num=0;
for(int i=1; i<=n+1; ++i){
temp=asd[i]; //需要备份一下
while(temp>x){ //第i个路标距离前一个位置(i-1)超过了x, 需要增设路标
num++;
//增设的位置应该尽量靠后, 直接设在距离上一路标x处
// int site=a[i-1]+x;
temp-=x; //不能直接对asd[i]进行操作
if(num>k){ //如果增设的路标数超过k, 返回false
return false;
}
}
}
return true;
}
int main()
{
scanf("%d %d %d", &dis, &n, &k);
for(int i=1; i<=n; ++i){
scanf("%d", &a[i]); //第i个路标距离起点的距离
asd[i]=a[i]-a[i-1];
r=max(r, asd[i]);
}
asd[n+1]=dis-a[n];
l=0;
r=max(r, asd[n+1]);
//二分空旷指数
while(l<r){
mid=(l+r)/2;
if(check(mid)){ //可以通过增设不超过k个路标, 满足相邻路标的距离均不超过mid
r=mid; //答案在左半部分, 且包含mid
}
else{ //增设k个路标, 无法满足相邻路标的距离不超过mid
l=mid+1; //答案在右半部分, 且不包含mid
}
}
printf("%d", l);
return 0;
}
P3743 kotori的设备
#include <bits/stdc++.h>
using namespace std;
const double eps=1e-5;
int n, p; //设备数、每秒可以充能的数量
int a[100010], b[100010]; //每秒消耗的能量、初始能量
long long suma, sumb; //每秒消耗的总能量
double l, r=1e10, mid;
double ans;
//看设备能否坚持x秒
bool check(double x)
{
double need=0;
for(int i=1; i<=n; ++i){
//x秒内设备i消耗的能量大于设备i初始的能量
if(a[i]*x>b[i]){
need+=a[i]*x-b[i]; //需要充能
}
}
return p*x>=need; //如果x秒内充能量满足需求, 返回true, 否则返回false
}
int main()
{
scanf("%d %d", &n, &p);
for(int i=1; i<=n; ++i){
scanf("%d %d", &a[i], &b[i]);
suma+=a[i];
sumb+=b[i];
}
//可以无限使用
if(p>=suma){ //每秒充能大于等于每秒耗能 //能得15分
printf("-1");
return 0;
}
while(l+eps<r){
// mid=(l+r)/2; //也能AC
mid=(l+r+eps)/2;
if(check(mid)){ //能坚持mid秒, 尝试更多大秒数, 解在右半部分
l=mid;
}
else{ //坚持不了mid秒, 尝试更小的秒数, 解在左半部分
r=mid-eps;
}
}
printf("%.10lf", l);
return 0;
}
P1024 [NOIP2001 提高组] 一元三次方程求解
#include <bits/stdc++.h>
using namespace std;
double a, b, c, d;
int cnt;
double cal(double x){
return (a*x*x*x + b*x*x + c*x +d);
}
int main()
{
cin >> a >> b >> c >> d;
for(double i=-100; i<100; i+=0.01){
// if(cnt==3) break;
if(abs(cal(i))<=0.01){
printf("%.2f ", i);
cnt++;
}
}
return 0;
}
P1314 [NOIP2011 提高组] 聪明的质监员
暴力代码, 能得35分
//10分暴力代码
#include <bits/stdc++.h>
using namespace std;
int n, m, mid, ql[200010], qr[200010];
long long w[200010], v[200010], wsum[200010], vsum[200010], l=1e6, r, s, y, ans;
bool flag;
int main()
{
scanf("%d %d %lld", &n, &m, &s);
ans=s;
for(int i=1; i<=n; ++i){
scanf("%lld %lld", &w[i], &v[i]);
l=min(l, w[i]);
r=max(r, w[i]);
}
for(int i=1; i<=m; ++i){
scanf("%d %d", &ql[i], &qr[i]);
}
//6*10^11
//暴力枚举每一个可能的w
for(int i=l; i<=r; ++i){ //10^6
y=0;
memset(wsum, 0, sizeof(wsum));
memset(vsum, 0, sizeof(vsum));
flag=false;
//求前缀和wsum
for(int j=1; j<=n; ++j){ //2*10^5
if(w[j]>=i){
wsum[j]=wsum[j-1]+1;
}
else{
wsum[j]=wsum[j-1];
}
}
//求前缀和vsum
for(int j=1; j<=n; ++j){ //2*10^5
if(w[j]>=i){
vsum[j]=vsum[j-1]+v[j];
}
else{
vsum[j]=vsum[j-1];
}
}
//计算
for(int j=1; j<=m; ++j){ //2*10^5
int lef=ql[j]; //检验区间的左边界
int righ=qr[j]; //检验区间的右边界
y+=(wsum[righ]-wsum[lef-1])*(vsum[righ]-vsum[lef-1]);
if(y>2*s){
flag=true;
break;
}
}
if(flag){
continue;
}
if(abs(s-y)<ans){
ans=abs(s-y);
if(ans==0){
break;
}
}
}
printf("%lld", ans);
return 0;
}
AC代码
#include <bits/stdc++.h>
using namespace std;
int n, m, ql[200010], qr[200010];
long long w[200010], v[200010], wsum[200010], vsum[200010], l=1e6, r, mid, s, y, ans;
//计算W为x时, 得到的y
long long cal(int x)
{
y=0;
memset(wsum, 0, sizeof(wsum));
memset(vsum, 0, sizeof(vsum));
//前缀和
for(int i=1; i<=n; ++i){ //2*10^5
if(w[i]>=x){
wsum[i]=wsum[i-1]+1; //求前缀和wsum
vsum[i]=vsum[i-1]+v[i]; //求前缀和vsum
}
else{
wsum[i]=wsum[i-1]; //求前缀和wsum
vsum[i]=vsum[i-1]; //求前缀和vsum
}
}
//计算
for(int i=1; i<=m; ++i){ //2*10^5
int lef=ql[i]; //检验区间的左边界
int righ=qr[i]; //检验区间的右边界
y+=(wsum[righ]-wsum[lef-1])*(vsum[righ]-vsum[lef-1]);
}
return y;
}
int main()
{
scanf("%d %d %lld", &n, &m, &s);
ans=s;
for(int i=1; i<=n; ++i){
scanf("%lld %lld", &w[i], &v[i]);
l=min(l, w[i]);
r=max(r, w[i]+1);
}
for(int i=1; i<=m; ++i){
scanf("%d %d", &ql[i], &qr[i]);
}
//找到第一个让y大于等于s的位置
while(l<r){
mid=(l+r+1)/2;
y=cal(mid);
if(y>s){ //如果W取mid, 得到的y大于s, 那么尝试将W取大一些, 这样才能让y变小一些, 看能否更逼近s
l=mid;
}
else if(y<s){//如果W取mid, 得到的y小于s, 那么尝试将W取小一些, 这样才能让y变大一些, 看能否更逼近s
r=mid-1;
}
else{ //如果y等于s, 直接输出0, 已经是最小了
printf("0");
return 0;
}
ans=min(ans, abs(s-y)); //方法2的计算
}
// printf("%lld", abs(cal(l)-s)); //会出错,因为s的两端都有可能
// printf("%lld", min(abs(cal(l)-s), abs(cal(l+1)-s))); //方法1的输出
printf("%lld", ans); //方法2的输出
return 0;
}
附1组测试数据
P1314_1.in
10 10 1475400
23954 25180
18805 2701
17195 5663
7044 13659
8139 30927
19774 25516
7472 4572
5999 6166
1185 13621
10414 26521
2 10
4 7
5 8
1 6
2 7
1 3
2 7
3 4
1 6
1 10
P1314_1.out
27196
P1083 [NOIP2012 提高组] 借教室
#include <bits/stdc++.h>
using namespace std;
int n, m, l, r, mid;
int a[1000001], d[1000001], s[1000001], t[1000001], diff[1000001];
bool check(int x)
{
memset(diff, 0, sizeof diff);
//差分
for(int i=0; i<x; i++)
{
diff[s[i]-1]-=d[i];
diff[t[i]]+=d[i];
}
for(int i=1; i<n; i++){
//前缀和
diff[i]+=diff[i-1];
}
for(int i=0; i<n; i++){
if(a[i]+diff[i]<0){
return false;
}
}
return true;
}
int main()
{
cin >> n >> m;
l=0;
r=m;
for(int i=0; i<n; i++){
cin >> a[i];
}
for(int i=0; i<m; i++){
cin >> d[i] >> s[i] >> t[i];
}
if(check(m)){
cout << "0";
return 0;
}
while(l<r){
mid=(l+r)/2;
if(check(mid)){
l=mid+1;
}
else{
r=mid;
}
}
cout << "-1\n" << r;
return 0;
// for(int i=1; i<=m; i++){
// if(!check(i))
// {
// cout << "-1" << endl << i;
// return 0;
// }
// }
}
P1843 奶牛晒衣服
数据太水,暴力能过,暴力AC代码
#include <bits/stdc++.h>
using namespace std;
int n, w[500010];
long long l=1, r, mid;
double a, b;
//检验在x秒内, 能否烘干所有的衣服
bool check(long long x)
{
long long need=0;
//枚举n件衣服
for(int i=1; i<=n; ++i){
//如果x秒内, 衣服可以自然晒干, 则不需要占用烘干机
if(x*a>=w[i]){
continue;
}
else{ //需要占用烘干机
need+=ceil((w[i]-x*a)/b);
if(need>x){
return false;
}
}
}
return true;
}
int main()
{
//a表示一秒自然晒干的湿度
//b表示一秒额外烘干的湿度
scanf("%d %lf %lf", &n, &a, &b);
//每件衣服的初始湿度
for(int i=1; i<=n; ++i){
scanf("%d", &w[i]);
r+=w[i];
}
for(long long i=1; i<=r; ++i){
if(check(i)){
printf("%lld", i);
return 0;
}
}
return 0;
}
二分AC代码
#include <bits/stdc++.h>
using namespace std;
int n, w[500010];
long long l=1, r, mid;
double a, b;
//检验在x秒内, 能否烘干所有的衣服
bool check(long long x)
{
long long need=0;
//枚举n件衣服
for(int i=1; i<=n; ++i){
//如果x秒内, 衣服可以自然晒干, 则不需要占用烘干机
if(x*a>=w[i]){
continue;
}
else{ //需要占用烘干机
need+=ceil((w[i]-x*a)/b);
if(need>x){
return false;
}
}
}
return true;
}
int main()
{
//a表示一秒自然晒干的湿度
//b表示一秒额外烘干的湿度
scanf("%d %lf %lf", &n, &a, &b);
//每件衣服的初始湿度
for(int i=1; i<=n; ++i){
scanf("%d", &w[i]);
r+=w[i];
}
while(l<r){
mid=(l+r)>>1;
if(check(mid)){
r=mid;
}
else{
l=mid+1;
}
}
printf("%d", r);
return 0;
}
P1281 书的复制
#include <bits/stdc++.h>
using namespace std;
int m, k, a[510], l, r, need, ans[510][3];
//判断k个人x秒能否全部抄完m本数
//转换为x秒抄完m本数用的人数是否小于等于k
bool check(int x)
{
need=1;
int cur=0;
ans[need][1]=m;
for(int i=m; i>=1; --i){
if(a[i]>x) return false;
if(cur+a[i]<x){ //第need个人x秒内能抄完第i本书
cur+=a[i];
continue;
}
else if(cur+a[i]>x){ //第i本书必须得下一个人来完成
ans[need][2]=i+1;
need++;
cur=a[i];
ans[need][1]=i;
}
else if(cur+a[i]==x){ //第need个人在x秒内刚好能抄完第i本书
ans[need][2]=i;
cur=0;
if(i>1){
need++;
ans[need][1]=i-1;
}
}
if(need>k){
return false;
}
}
if(cur>0){
ans[need][2]=1;
}
return true;
}
int main()
{
scanf("%d %d", &m, &k); //m本书、k个人去抄写
for(int i=1; i<=m; ++i){
scanf("%d", &a[i]); //每本书的页数
l=max(l, a[i]);
r+=a[i];
}
for(int i=l; i<=r; ++i){
if(check(i)){ //如果i秒能抄完
for(int j=need; j>=1; --j){
printf("%d %d\n", ans[j][2], ans[j][1]);
}
return 0;
}
}
return 0;
}
提供1个测试点
P1281_1.in
1 1
1
P1281_1.out
1 1
二分代码
#include <bits/stdc++.h>
using namespace std;
int m, k, a[510], l, r, mid, need, ans[510][3];
//判断k个人x秒能否全部抄完m本数
//转换为x秒抄完m本数用的人数是否小于等于k
bool check(int x)
{
need=1;
int cur=0;
ans[need][1]=m;
for(int i=m; i>=1; --i){
if(a[i]>x) return false;
if(cur+a[i]<x){ //第need个人x秒内能抄完第i本书
cur+=a[i];
continue;
}
else if(cur+a[i]>x){ //第i本书必须得下一个人来完成
ans[need][2]=i+1;
need++;
cur=a[i];
ans[need][1]=i;
}
else if(cur+a[i]==x){ //第need个人在x秒内刚好能抄完第i本书
ans[need][2]=i;
cur=0;
if(i>1){
need++;
ans[need][1]=i-1;
}
}
if(need>k){
return false;
}
}
if(cur>0){
ans[need][2]=1;
}
return true;
}
int main()
{
scanf("%d %d", &m, &k); //m本书、k个人去抄写
for(int i=1; i<=m; ++i){
scanf("%d", &a[i]); //每本书的页数
l=max(l, a[i]);
r+=a[i];
}
while(l<r){
mid=(l+r)>>1;
if(check(mid)){ //如果mid秒能抄完
r=mid;
}
else{
l=mid+1;
}
}
check(l);
for(int j=need; j>=1; --j){
printf("%d %d\n", ans[j][2], ans[j][1]);
}
return 0;
}
P1883 函数
暴力做法 超时,0分
#include <bits/stdc++.h>
using namespace std;
int t, n, a[10010], b[10010], c[10010];
double l, r, mid, ans, eps=1e-5;
//x取某一个值时, 得到的F(x)
double cal(double x)
{
double mx=-1e7, sum;
for(int i=1; i<=n; ++i){
sum=a[i]*x*x+b[i]*x+c[i];
mx=max(mx, sum);
}
return mx;
}
int main()
{
scanf("%d", &t);
while(t--){ //t<10
scanf("%d", &n); //n<=10^4
ans=1e9;
for(int i=1; i<=n; ++i){
scanf("%d %d %d", &a[i], &b[i], &c[i]);
}
for(double i=0; i<=1000; i+=eps){
ans=min(ans, cal(i));
}
printf("%.4lf\n", ans);
}
return 0;
}
三分做法,本题单峰函数
#include <bits/stdc++.h>
using namespace std;
int t, n, a[10010], b[10010], c[10010];
double l, r, mid1, mid2, eps=1e-9;
//x取某一个值时, 得到的F(x)
double cal(double x)
{
double mx=-1e7, sum;
for(int i=1; i<=n; ++i){ //n<=10^4
sum=a[i]*x*x+b[i]*x+c[i];
mx=max(mx, sum);
}
return mx;
}
int main()
{
scanf("%d", &t);
while(t--){ //t<10
scanf("%d", &n); //n<=10^4
for(int i=1; i<=n; ++i){
scanf("%d %d %d", &a[i], &b[i], &c[i]);
}
l=0; //记得每次都得赋值为0
r=1000;
//三分可能的区间
while(l+eps<r){ //log(10^15)<50
mid1=(2*l+r)/3; //三等分点 O(n) 10^4
mid2=(l+2*r)/3; //三等分点
if(cal(mid1)<cal(mid2)){ //题目要求最小值
//说明答案偏向在左部
r=mid2-eps;
}
else{ //答案偏向在右部
l=mid1;
}
}
printf("%.4lf\n", cal(l));
}
return 0;
}
P4343 [SHOI2015]自动刷题机
#include <bits/stdc++.h>
using namespace std;
int n, k, a[100010];
long long l=1, r=1e14, mid;
bool asd;
//达到m行就可以提交AC一题
long long check(long long m)
{
long long acnum=0, cur=0;
for(int i=1; i<=n; ++i){
cur+=a[i];
if(cur<0){
cur=0;
}
else if(cur>=m){
cur=0;
acnum++;
}
}
return acnum;
}
int main()
{
scanf("%d %d", &n, &k);
for(int i=1; i<=n; ++i){ //n条日志
scanf("%d", &a[i]); //每条日志写的代码数
}
while(l<r){
mid=(l+r)>>1;
//求最小值
//如果恰好能切了k道题, 尽量往小
//如果切的题小于等于k, 说明要求高了, 尽量往小了走
if(check(mid)<=k){
r=mid;
}
else if(check(mid>k)){ //如果切的题大于k, 说明要求低了, 往大了走
l=mid+1;
}
}
if(check(l)==k){
printf("%lld ", l);
asd=true;
}
l=1, r=1e14;
while(l<r){
mid=(l+r+1)>>1;
//求最大值
//如果恰好能切了k道题, 尽量往大
//如果切的题大于等于k, 说明要求低了, 往大了走
if(check(mid)>=k){
l=mid;
}
else if(check(mid<k)){ //如果切的题小于k, 说明要求高了, 往小了走
r=mid-1;
}
}
if(check(l)==k){
printf("%lld", l);
asd=true;
}
if(!asd){
puts("-1");
}
return 0;
}