8-27复习(写题)报告

经过整理,好像很多模拟赛题都用到了二分答案,所以今天写了一点二分答案的题('◡')。

特爱二分答案,在只添加一个\(log(n)\)的时间复杂度的情况下让找出答案变成判断答案,大大降低了我们解决问题的难度。

因此很多题都适用,考试如果没有思路可以考虑考虑二分答案哦!


P2658 汽车拉力比赛

题目大意:给出一个\(n*m\)的矩阵,每一个点有一个海拔,有一些矩阵上的点打了标记,求打了标记的点互相之间抵达海拔差的最小值,一个点走向可以和上下左右四个方向。\((n,m\le 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\)个点,每个点有一个坐标和需要到达的时间段,求最小的最大速度\((n\le 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\)个设备,每个设备有一个能量总值和单位时间耗能值,你可以随时给一个设备充能(同一个时间只能给一个设备充能),求所有设备都还有能量的最长时间。\((n\le 100000)\)

一样的套路,二分答案,二分一个最大时间,然后\(O(n)\)判断是否可以达到目标,OVER!

手起。。要不自己练练,就不贴代码了(>▽<)(就是口胡出来之后不愿意打


除了二分答案外,还被同机房的dalao拉去写了一道期望DP(期望蒟蒻的我瑟瑟发抖

但勉强还是写下来了。。

P6046 纯粹容器

题目大意:有\(n\)个罐子排成一排,随机选择一对互相决斗,强度大的存活,现在给出每个罐子的强度,求每个罐子的存活期望\((n\le 50)\)

这一题还是比较有意思的(差点想往二分答案的方向想˶´⚰︎`˵)直接上算法吧!

由于\(n\le 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\times b^{-1}~\equiv~x~(~mod~m~) \]

\[\Rightarrow a\times 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;
}

好弱呀,才写了这么一点题。。明天继续干!!

posted @ 2020-08-27 21:23  Chester1011  阅读(115)  评论(0编辑  收藏  举报
/*simplememory*/