kauaiの水题记录——二分

1. 中位数#

第一眼看这道题,想到可以先开一个数组,每加入一个数就用sort​去排序,然后取中间的数,复杂度是O(n2logn),所以可以使用vertor,因为vertor​​支持在数组中随意插入一个数,所以我们只要找到这个数在哪里就行了,进而想到可以二分,然后就可以了。


这里介绍一下vector

#include <vector>//头文件
vector<int> s;
s.push_back(a);//尾部插入
s.pop_back();//尾部删除
vertor<int>::iterator it;//声明迭代器
int k=*s.begin();//s.bejin()会返回第一个元素的迭代器,可以用*解除引用
s.insert(s.begin()+i,a)//在第i+1个元素中插入a
s.erase(s.begin()+1)//删除第二个元素
s.erase(s.begin()+i,s.end()+j)//删除区间[i,j-1],区间从零开始
int y=*upper_bound(a.begin(),a.end(),x)//查找第一个大于x的数

2. 山#

题目中说要我们找最小的y坐标,想到可以二分答案,接着想如何判断二分出来的情况是否合法,把每两个相邻折点连成的线段想象成一个一次函数y=kx+b​,如果灯的位置在每条直线的上方就是合法的,作一条y=mid​的直线,和每条一次函数就会有交点,限制灯的横坐标的范围x1,x2​然后进行分类讨论,如果k>0​,显然横坐标要在交点的左边,反之同理,要注意的是我们还要判断k=0​的清况,此时要注意的就是纵坐标的,因为灯一定要比这条直线高,最后判断范围是否合法即可。我们可以预处理出每条直线的k,b以此来减小常数,要注意实数域上的二分。

    while(l+eps<r){
	double mid=(l+r)/2.0;
	if(check(mid)){
	    r=mid;
	    ans=mid;
	}
	else l=mid;
    }

一般eps要比题目要求的精度多两位。

3. 弹幕考察#

这道题如果理解了题意还是比较简单的,可以看作是一个区间覆盖问题,首先我们可以打一个O(nm)的暴力,对于每一次询问都去枚举每一区间,看是否满足要求,然后累加答案,这样可以拿30分。

其实我们可以把每一个区间都看作两个端点,并开一个l[],r[]​数组记录位置,询问的时候二分查找l[]​中第一个大于等于查询区间右端点的数的位置,r[]​中第一个大于等于查询区间左端点数的位置,然后相减就行了。

4. 赛道修建#

这道题我也不是太懂,就随便口胡一下,想到二分答案,二分最小的赛道长度,对于每一个结点,我们可以存一个val,从下往上传,然后放进multiset中,分两种情况讨论,如果val>=k​说明这个点和它的子树已经可以满足条件了,答案+1,否则就把当前这条边放进multiset中,接着看第二种情况,以这个点为根的子树的两条边相加会大于等于k​​,贪心地想,两个数是不是越小越好,因为我们要把大的数上传到上面去,所以我们可以在multiset里二分去找这个数,然后把这两个点删除,ans++,找不到就和要上传的max进行比较,要说明一下,因为一个子树肯定只有一条边连接着上面的结点,所以要去找max​。最后判断ans是不是大于m就可以知道是不是合法的了。

核心代码:


int dfs(int x,int fa,int k){
    s[x].clear();
    int val;
    for(int i=head[x];i;i=nxt[i]){
	int y=ver[i];
	if(y==fa) continue;
	val=dfs(y,x,k)+edge[i];
	if(val>=k) tot2++;
	else{
	    s[x].insert(val);
	}
    }
    int Max=0;
    while(!s[x].empty()){
	if(s[x].size()==1) return max(Max,*s[x].begin());
	it=s[x].lower_bound(k-*s[x].begin());
	if(it==s[x].begin()&&s[x].count(*it)==1) it++;
	if(it==s[x].end()){
	    Max=max(Max,*s[x].begin());
	    s[x].erase(s[x].find(*s[x].begin()));
	}
	else{
	    tot2++;
	    s[x].erase(s[x].find(*it));
	    s[x].erase(s[x].find(*s[x].begin()));
	}
    }
    return Max;

5. [SHOI2015]自动刷题机#

说实话,这道题真的比较水,感觉不值得评蓝,题目要我们求最大和最小值,所以想到二分,我们可以二分两次,然后对着题意模拟判断是否合法就行了。

比较难的就是如何二分最大和最小值了,其实就是把ans​放到lr区间里了。

    while(l<=r){
	int mid=(l+r)>>1;
	int Quick_kk=check(mid);
	if(Quick_kk<=k){//求最小值
	    r=mid-1;
	    if(Quick_kk==k){ans1=mid;}
	}
	else l=mid+1;
    }
    l=1,r=1e18;
    while(l<=r){//求最大值
	int mid=(l+r)>>1;
	int Quick_kk=check(mid);
	if(Quick_kk>=k){
	    l=mid+1;
	    if(Quick_kk==k){ans2=mid;}
	}
	else r=mid-1;
    }

6. [JXOI2017]加法#

首先二分答案,操作后的最小值,接着贪心地想,除非一定要加,否则就不加,我们可以开一个优先队列,里面存放每个区间的右端点,列举到当前的数时,就在右端点最大的区间里加,因为左边的数已经加完了,用差分维护区间加法,最后判断操作次数是否小于等于 k​,和操作完后是否大于等于最小值。

7. [POI2013]LUK-Triumphal arch#

首先二分最小的 k​​​​​​,然后统计每个结点下一级儿子的个数存在sum[]​​​​中,用 f[x]​​​​​ 数组统计以x为根的字数内需要支援的次数,初始化为 sum[x]k​​​​,如果f​​​​​​数组大于 0​​​ 则向上传,如果小于零也没什么用,因为是从上往下染色的,最后判断f[1]​​​​是否小于等于 0​​​ 。

8. [HEOI2016/TJOI2016]排序#

sort​ 可以拿 80​​ 分!

二分第 p 位上的数到底是什么,考虑 check() ,把序列里大于等于 mid 的数改为 1 ,其余为 0,这样排序时只需要改变 1,0 的位置即可,显然可以用线段树维护,最后判断第 p 位上的数是否为 1 ,是则为合法的答案,因为为 1 则说明a[p]>=mid,二分求出 mid 的最大值也就是等于 a[p] 的情况。

posted @   Miraii  阅读(46)  评论(2编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
主题色彩