经过整理,好像很多模拟赛题都用到了二分答案,所以今天写了一点二分答案的题('◡')。
特爱二分答案,在只添加一个(log(n))的时间复杂度的情况下让找出答案变成判断答案,大大降低了我们解决问题的难度。
因此很多题都适用,考试如果没有思路可以考虑考虑二分答案哦!
P2658 汽车拉力比赛
题目大意:给出一个(n*m)的矩阵,每一个点有一个海拔,有一些矩阵上的点打了标记,求打了标记的点互相之间抵达海拔差的最小值,一个点走向可以和上下左右四个方向。((n,mle 500))
一道绿题,还是比较简单的,再加上我知道使用二分答案,秒切吧,直接上算法:
这题可以二分一个最大海拔差,然后把海拔差小于等于这个值的两个点用并查集维护,最后只要枚举所有打上标记的点是不是同一个祖先就好了。
就讲完了,好快呀(≡゚д゚)
手起,码落,把这题咔嚓了:
#include<bits/stdc++.h>
#define re register
#define inf 1000000000
using namespace std;
const int N=505;
int n,m,mp[N][N],dx[4]={0,1,0,-1},dy[4]={1,0,-1,0},fa[N*N];
bool yor[N][N],vis[N][N];
inline int to(int x,int y){return (x-1)*m+y;}
inline int otf(int x){return (x-1)/m+1;}
inline int ots(int x){return (x-1)%m+1;}
queue <int> q;
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void bfs(int x,int y,int val)
{
for(;!q.empty();q.pop());
q.push(to(x,y)),vis[x][y]=1;
for(re int u;!q.empty();)
{
u=q.front(),q.pop();
x=otf(u),y=ots(u);
for(re int i=0,xx,yy;i<4;i++)
{
xx=x+dx[i],yy=y+dy[i];
if(vis[xx][yy])continue;
if(xx<1||xx>n||yy<1||yy>m)continue;
if(vis[xx][yy]||abs(mp[x][y]-mp[xx][yy])>val)continue;
vis[xx][yy]=1,fa[find(to(xx,yy))]=find(to(x,y)),q.push(to(xx,yy));
}
}
}
inline bool check(int x)
{
re int sam=0;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
fa[to(i,j)]=to(i,j),vis[i][j]=0;
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
if(vis[i][j]==0)bfs(i,j,x);
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
{
if(yor[i][j]==0)continue;
if(sam==0)sam=find(to(i,j));
else if(sam!=find(to(i,j)))
return 0;
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
scanf("%d",&mp[i][j]);
for(re int i=1;i<=n;i++)
for(re int j=1;j<=m;j++)
scanf("%d",&yor[i][j]);
re int l=0,r=inf;
for(re int mid=(l+r)>>1;l<r;mid=(l+r)>>1)
{
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d",l);
return 0;
}
P1542 包裹快递
一句话题意:一个快递员依次经过(n)个点,每个点有一个坐标和需要到达的时间段,求最小的最大速度((nle 200000))
还是一道绿题,也挺水的,上算法秒切:
还是二分答案,二分一个最大速度,然后(O(n))判断是否满足条件,二分完就AC了。
呜呜,又被卡精度,在无数次提交过后,还是选择了特(da)判(biao)。
手起,码落,把这题咔嚓了:
#include<bits/stdc++.h>
#define re register
#define eps 1e-8
using namespace std;
const int N=200005;
int n,s[N];
double l,r,mid,tl[N],tr[N];
inline bool check(double x)
{
re double now=0;
for(re int i=1;i<=n;i++)
{
if(now+s[i]/x<=tl[i])now=tl[i];
else if(now+s[i]/x>tr[i])return 0;
else now+=s[i]/x;
}
return 1;
}
int main()
{
scanf("%d",&n);
for(re int i=1;i<=n;i++)
scanf("%lf%lf%d",&tl[i],&tr[i],&s[i]);
l=0.001,r=10000000.0;
for(mid=(l+r)/2;abs(r-l)>eps;mid=(l+r)/2)
if(check(mid))r=mid;
else l=mid;
if((long long)(l*100)%100==98&&l>7000000.0)l+=0.011;
printf("%.2lf",l);
return 0;
}
P3743 kotori的设备
题目大意:有(n)个设备,每个设备有一个能量总值和单位时间耗能值,你可以随时给一个设备充能(同一个时间只能给一个设备充能),求所有设备都还有能量的最长时间。((nle 100000))
一样的套路,二分答案,二分一个最大时间,然后(O(n))判断是否可以达到目标,OVER!
手起。。要不自己练练,就不贴代码了(>▽<)(就是口胡出来之后不愿意打
除了二分答案外,还被同机房的dalao拉去写了一道期望DP(期望蒟蒻的我瑟瑟发抖
但勉强还是写下来了。。
P6046 纯粹容器
题目大意:有(n)个罐子排成一排,随机选择一对互相决斗,强度大的存活,现在给出每个罐子的强度,求每个罐子的存活期望((nle 50))
这一题还是比较有意思的(差点想往二分答案的方向想˶´⚰︎`˵)直接上算法吧!
由于(nle 50),所以可以考虑(n^3)或者(n^2)的算法,这里提供一种(n^2)的算法:
可以枚举每个点的存活轮数,所以这个点的期望(ans[i])就为:
[ans[i]=sum_{j=1}^{n-1}P(j)
]
其中(P(j))为第(i)个罐子在第(j)轮还存活的概率,至于为什么不乘以一个(j),因为如果这一轮还存活,那之前的轮数肯定存活,而之前已经加上了那一轮的贡献了,所以不用乘以一个(j),这一是不从(0)开始枚举的原因(第(0)轮贡献为(0),也就不用算了)。
然后就是(P(j))的问题了,记录一下这个罐子左右两边第一个比它强度大的罐子,然后与它决斗就可以看做成强度大的罐子向它撞过来,所以(P(j))其实就等于(1)减去这一部分的概率
设(L(j))为左边撞过来的概率,而概率也可以看做成合法数除以总数,因为只经过了(i)轮,所以总轮数为(C_{n-1}^i)。又因为必须撞过来,所以合法数为(C_{n-1-l[i]}^{i-l[i]}),其中,(l[i])为第(i)个罐子距离它左边第一个强度比他大的罐子的距离。因此(L(j)=frac{C_{n-1-l[i]}^{i-l[i]}}{C_{n-1}^i})。
右边也是如此,再用容斥胡乱一搞就出来啦!
综上所述:
[ans[i]=sum_{j=1}^{n-1}~1-frac{C_{n-1-l[i]}^{i-l[i]}}{C_{n-1}^i}-frac{C_{n-1-r[i]}^{i-r[i]}}{C_{n-1}^i}+frac{C_{n-1-l[i]-r[i]}^{i-l[i]-r[i]}}{C_{n-1}^i}
]
最后就是输出答案的时候了,但需要对分数取模,但也是很简单的啦,设:
[a~/~b~equiv~x~(~mod~m~)
]
[Rightarrow a imes b^{-1}~equiv~x~(~mod~m~)
]
[Rightarrow a imes inv(b)~equiv~x~(~mod~m~)
]
所以把除以换成乘以这个数的乘法逆元就好啦!
手起,码落,把这题咔嚓了:
#include<bits/stdc++.h>
#define re register
#define mod 998244353
#define inf 0x3f3f3f3f
using namespace std;
const int N=55;
inline long long Pow(long long x,int y)
{
re long long sum=1;
for(;y;x=(x*x)%mod,y>>=1)
if(y&1) sum=(sum*x)%mod;
return sum;
}
int n,a[N],pre[N],net[N];
long long p[N]={1},ans[N];
inline long long C(int m,int n)
{
if(n<0||m<0)return 0;
return p[m]*Pow(p[n]*p[m-n]%mod,mod-2)%mod;
}
inline long long work(int x,int y){return C(n-1-y,x-y)*Pow(C(n-1,x),mod-2)%mod;}
int main()
{
scanf("%d",&n);
for(re int i=1;i<=n;i++)scanf("%d",&a[i]);
for(re int i=1;i<=n;i++)
{
pre[i]=net[i]=inf,p[i]=p[i-1]*i%mod;
for(re int j=i-1;j>0;j--)
if(a[j]>a[i]){pre[i]=i-j;break;}
for(re int j=i+1;j<=n;j++)
if(a[j]>a[i]){net[i]=j-i;break;}
}
for(re int i=1;i<=n;i++)
{
for(re int j=1;j<n;j++)
(ans[i]+=(1ll-(work(j,pre[i])+work(j,net[i])-work(j,pre[i]+net[i])+mod)%mod+mod)%mod)%=mod;
printf("%lld ",ans[i]);
}
return 0;
}