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\),那么一次操作就能让这个排列有序。
所以
注意要特判下排列本身就有序的情况。
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;
}