Codeforces #689 A - E 题解

前言

上橙了,可喜可贺qwq。

A - String Generation

题目大意

构造一个长度为 \(n\) 且只有 \(a,b,c\) 三种字符的字符串,使得最长回文子串长度不超过 \(k\)

解题思路

显然 \(abcabcabc...\) 构造的字符串最长回文子串一定是 \(1\)

Code

int t,n,k;

signed main()
{
	t=read();
	while(t--)
	{
		n=read();k=read();
		for(int i=1;i<=n;++i)
			printf("%c",i%3==1?'a':(i%3==2?'b':'c'));
		printf("\n");
	}
	return 0;
}

B - Find the Spruce

题目大意

求出矩阵中有多少个高度任意的金字塔

解题思路

\(n \le 500\)

直接枚举矩阵位置,然后再枚举高度,前缀和判断是否合法。

注意边界的处理。

Code

const int N=505;
int t,n,m,sum[N][N],ans;
char mp[N][N];

signed main()
{
	t=read();
	while(t--)
	{
		n=read();m=read();
		for(int i=1;i<=n;++i)
			scanf("%s",mp[i]+1);
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				sum[i][j]=sum[i][j-1]+(mp[i][j]=='*');
		ans=0;
		for(int i=1;i<=n;++i)
			for(int j=1;j<=m;++j)
				for(int k=1;k<=n-i+1&&j+k-1<=m&&j-k+1>=1;++k)
					if(sum[i+k-1][j+k-1]-sum[i+k-1][j-k]==(k-1)*2+1)
						ans++;
					else break;
		printf("%lld\n",ans);
	}
	return 0;
}

C - Random Events

题目大意

给一个排列,有 \(m\) 种操作,第 \(i\) 种操作会将排列的前 \(a_i\) 个数排序,有 \(p_i\) 的概率发生,求序列最终有序的概率。

解题思路

\(p\) 为满足 \(p+1 \sim n\) 这段有序的最小值,\(x\) 为排列有序的概率,\(y\) 为排列无序的概率。

那么显然若 \(1 \le a_i \le p-1\),那么这个操作肯定没用。而如果 \(p \le a_i \le n\),那么一次操作就能让这个排列有序。

所以

\[\begin{aligned}x & = 1-y \\ & = 1-\prod\limits_{i=1}^n\{1-p_i\ |\ a_i \ge p\}\end{aligned} \]

注意要特判下排列本身就有序的情况。

Code

const int N=1e5+5;
int t,n,m,a[N],mmax;
double ans;

signed main()
{
	t=read();
	while(t--)
	{
		n=read();m=read();
		mmax=0;ans=1;
		for(int i=1;i<=n;++i)
			a[i]=read();
		for(int i=n;i>=1;--i)
			if(a[i]!=i)
			{
				mmax=i;
				break;
			}
		
		for(int i=1;i<=m;++i)
		{
			int x=read();
			double y;
			scanf("%lf",&y);
			if(x>=mmax) ans*=(1-y);
		}
		if(mmax==0)
		{
			printf("1\n");
			continue;
		}
		printf("%.6lf\n",1-ans);
	}
	return 0;
}

D - Divide and Summarize

题目大意

给一个序列 \(a\) ,设 \(mid=\dfrac{\max\{a_i\} + \min\{a_i\}}{2}\),则每次可以丢掉大于 \(mid\) 的数或小于等于 \(mid\) 的数。

\(q\) 次询问,每次回答能否通过对 \(a\) 操作来使得 \(a\) 中所有数的和等于某个数。

解题思路

显然序列 \(a\) 种元素的顺序不会影响答案,所以可以先排序,这样不论怎么丢,剩下的数都是一个区间。

接下来直接模拟即可,处理出所有情况,查找小于或大于每个数可以用二分来实现。

Code

const int N=1e5+5;
int t,n,q,a[N],sum[N];

map<int,int> mp;

inline void binary(int l,int r);
inline int bin(int l,int r,int x);

signed main()
{
	t=read();
	while(t--)
	{
		n=read();q=read();
		mp.clear();
		for(int i=1;i<=n;++i)
			a[i]=read();
		sort(a+1,a+1+n);
		for(int i=1;i<=n;++i)
			sum[i]=sum[i-1]+a[i];
		binary(1,n);
		while(q--)
		{
			int x=read();
			if(mp[x]) printf("Yes\n");
			else printf("No\n");
		}
	}
	return 0;
}

inline void binary(int l,int r)
{
	if(l>r) return;
	int mid=(a[l]+a[r])>>1;
	mp[sum[r]-sum[l-1]]=1;
	if(l==r) return;
	int pos=bin(l,r,mid);
	if(pos!=r) binary(l,pos);
	binary(pos+1,r);
}

inline int bin(int l,int r,int x)
{
	int res=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(a[mid]<=x)
		{
			res=mid;
			l=mid+1;
		}
		else r=mid-1;
	}
	return res;
}

E - Water Level

题目大意

有一个数 \(k\),必须时刻保持这个数在 \([l,r]\) 中。

现在有 \(t\) 天,每天 \(k\) 都会减少 \(x\),但每天开始前可以选择将 \(k\) 加上 \(y\)

问能否保证 \(k\) 始终在 \([l,r]\) 中的条件下度过这 \(t\) 天。

\(1 \le l,k,r,t,y \le 10^{18},1 \le x \le 10^6\)

解题思路

观察到这几个数中唯一小到可以枚举的只有 \(x\) ,因此考虑把复杂度转移到 \(x\) 上。

我们分两种情况讨论:

如果 \(y < x\),你么每天肯定都要加,而每天都加这个数每天也会减少 \(x-y\),因此看看能否减少 \(t\) 天即可。

而如果 \(y \ge x\),就会有点麻烦了。

一个显然的结论是,只在 \(k\) 减去 \(x\) 后会小于 \(l\) 的那天加 \(y\) 是最优的。

因为 \(y>x\),所以加完后一定可以保证减去 \(x\) 后的数仍大于 \(l\)

因此可以求出每一个减去 \(x\) 后小于 \(l\) 要过多少天,然后加上 \(y\) ,不断循环。

当出现下面这种情况时,一定不能完成:

\(\space\space\space\space\space1.\) \(k\) 不能再减 \(x\),但是一旦加上 \(y\) 就会大于 \(r\)

而如果出现以下两种情况,那么一定能完成:

\(\space\space\space\space\space1.\) 天数超过 \(t\)

\(\space\space\space\space\space2.\) 当前的这个 \(k\) 在之前出现过(即出现了一个循环)

因为不能在减少 \(x\)\(k\) 一共只有 \(x\) 个,这就保证了程序一定在 \(x\) 次运算内结束,因此复杂度为 \(\text{O}(x)\)

Code

const int N=1e6+5;
int k,l,r,t,x,y,fl=1,vis[N];

signed main()
{
	k=read();l=read();r=read();t=read();x=read();y=read();
	if(y<x)
	{
		int now=0;
		while(k+y>r&&now<t) k-=x,now++;
		if((k-l)/(x-y)>=t-now) printf("Yes\n");
		else printf("No\n");
		return 0;
	} 
	int now=(k-l)/x;
	k-=now*x;
	while(now<t)
	{
		if(vis[k-l])
			break;
		vis[k-l]=1;
		if(k+y>r)
		{
			printf("No\n");
			return 0;
		}
		k+=y;
		int tmp=(k-l)/x;
		now+=tmp;
		if(now>=t) break;
		k-=tmp*x;
	}
	printf("Yes\n");
	return 0;
}
posted @ 2020-12-12 01:05  Blackbird137  阅读(143)  评论(1编辑  收藏  举报