牛客小白月赛#59
A.我会开摆
-
题意
- 给定n=4的方阵,问是否存在n=2的方阵中四个格子全是一个字符的情况
-
题解
- 直接枚举所有点当n=2方阵的左上角,看是否符合题目要求
- 注意:检查n=2的方阵时,要先排除某点为左上角没有n=2方阵情况
-
代码
#include <iostream>
using namespace std;
char a[4][4];
bool check(int x,int y) {//检查以该点为左上角的,n=2的方阵是否符合要求
char c=a[x][y];
for(int i=x;i<x+2;i++)
for(int j=y;j<y+2;j++)
if(i<4 && j<4 && a[i][j]!=c) return 0;
if(x<3 && y<3) return 1;
return 0;
}
bool solve() {
for(int i=0;i<4;i++)
for(int j=0;j<4;j++)
cin>>a[i][j];
for(int i=0;i<4;i++)//枚举所有点
for(int j=0;j<4;j++)
if(check(i,j)) return 1;
return 0;
}
int main() {
int t; cin>>t;
while(t--) cout<<(solve() ? "Yes":"No")<<'\n';
return 0;
}
B.走廊的灯
-
题意
- 路上有n盏灯,0代表灭的,1代表亮的,2代表闪烁,可以看成灭的也可以看成亮的
- 问最长的状态一样的连续的灯的数量为多少
-
题解
- 双指针O(n);每次找到12连续段或者02连续段
- 暴力O(n*n);数据比较水,不需要双指针。只要枚举以每一个点为起点往后找状态相同的灯,记录最大值即可
- 注意(代码实现为找同一状态的灯要注意这条,如果遍历时代码实现只看不符合要求的那就不需要看这条):如果2开头,参照的灯的状态为,从左往右第一个不为2的灯的状态
-
代码
双指针
#include <iostream>
#include <cstring>
using namespace std;
void solve() {
int n; cin>>n;
string s; cin>>s;
int res=1;
for(int i=0;i<n;i++) {//统计02连续段,因为02段是被1隔开,所以只要不是1就行
if(s[i]!='1') {
int j=i;
while(j<n && s[j]!='1') j++;
res=max(res,j-i);
j--; i=j;
}
}
for(int i=0;i<n;i++) {//统计12连续段,同理
if(s[i]!='0') {
int j=i;
while(j<n && s[j]!='0') j++;
res=max(res,j-i);
j--; i=j;
}
}
cout<<res<<'\n';
}
int main() {
int t; cin>>t;
while(t--) solve();
return 0;
}
暴力
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
void solve() {
int n; cin>>n;
string s; cin>>s;
int res=1;
for(int i=0;i<n;i++) {
int j=i;
while(j<n && s[j]=='2') j++;//找到第一个不为2的灯,以它作为参照状态
char c=s[j];
while(j<n && (s[j]=='2' || s[j]==c)) j++;//找后面连续的灯
res=max(res,j-i);//更新答案
}
cout<<res<<'\n';
}
int main() {
int t; cin>>t;
while(t--) solve();
return 0;
}
C.输出练习
-
题意
- 从小到大输出区间[l,r]中,k的非负整数次方的值
- l,r,k范围 [0,2^63)
-
题解
- 除去特例的0,1;当k=2时,l=0,r=2^63-1,会有最多个输出,也才64个。所以直接暴力即可
- 注意特判。k=1时,直接输出1即可,否则暴力和会炸;k=0时,0^0=1,但是
0^1
=0,0^2
=0,即如果区间符合要求,那么k=0会有两个输出。麻麻麻这里WA麻了,好无语 - 注意数据范围,while(res<=r)这样写会爆long long,应该写成while(res<=r/k) ,又WA好几发,烦死了
-
代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ULL;
void solve() {
ULL l,r,k;
cin>>l>>r>>k;
ULL res=1,cnt=0;
if(k==0 && l<=0 && r>=0) cout<<0<<' ',cnt++;//特判
if(l<=1 && r>=1) cout<<1<<' ',cnt++;
while(k&&k!=1 && res<=r/k) {//数据范围,这样写防止爆long long
res*=k;
if(res>=l && res<=r) cout<<res<<' ',cnt++;
}
if(cnt==0) cout<<"None.";
cout<<'\n';
}
int main() {
int t; cin>>t;
while(t--) solve();
return 0;
}
D.国际象棋
-
题意
- 竖直放置的n*m的棋盘,每次选一列放一枚棋子,重力原因棋子落到最下面
- 对于第i个放置的棋子,i为奇数时,其所放的颜色为黑色,偶数为白色棋子
- k子连珠定义为:横竖撇捺四个方向存在某个方向有k个相同颜色棋子
- 一共放t颗棋子,若出现k子连珠则游戏结束,保证有解,问游戏结束时放置了多少棋子
-
题解
-
模拟题。
需要维护的变量: 棋盘的状态 -> g[n][m]=1/2,来表示棋盘及棋子的状态 当前列放棋子落在第几行 -> h[i]表示第i列的高度(行数) 需要维护的操作: 检验当前有无k子连珠 -> check(x,y),以(x,y)为中心去检验四种方向,每种方向正负两个方向去记录连续的相同颜色的棋子数量,数量大于等于k即符合要求
-
-
代码
#include <iostream>
using namespace std;
const int N=1005;
int n,m,k,t;
int g[N][N],h[N];
int dx[4]={1,0,1,-1},dy[4]={0,-1,-1,-1};//单位向量的定义为横竖撇捺四个方向
bool check(int x,int y) {//检验以(x,y)为中心是否存在k子连珠
for(int d=0;d<=3;d++) {//横竖撇捺四种方向检查
int cnt=0;//此方向连续的相同颜色棋子数量
int dxx=dx[d],dyy=dy[d];//偏移量
int tx=x,ty=y;
//不越界且连续颜色相同,正方向有多少个
while(1<=tx&&tx<=n&&1<=ty&&ty<=m && g[tx][ty]==g[x][y]) {
cnt++; tx+=dxx; ty+=dyy;
}
//不越界且连续颜色相同,负方向有多少个
tx=x,ty=y;
while(1<=tx&&tx<=n&&1<=ty&&ty<=m && g[tx][ty]==g[x][y]) {
cnt++; tx-=dxx; ty-=dyy;
}
cnt--;//多记录了(x,y)自身,去除
if(cnt>=k) return 1;
}
return 0;
}
int main() {
cin>>n>>m>>k>>t;
for(int i=1;i<=t;i++) {//注意是t个棋子
int x; cin>>x;
h[x]++;//选择该列,该列的高度增加;注意题目的棋盘是从1开始的
g[h[x]][x]=((i&1) ? 2:1);//black:2;注意颜色是由输入顺序定义的
if(check(h[x],x)) {cout<<i<<'\n'; return 0;}//有答案输出
}
return 0;
}
E.弹珠碰撞
-
题意
- 长度为n的线段上有m个弹珠,给定每个弹珠的方向左/右,及坐标
- 弹珠每秒走一个单位,发生碰撞会停滞一秒,且两珠方向互换,多次碰撞停滞时间叠加
- 当弹珠走到0,或者n+1认为掉落
- 问线段上最后掉落的弹珠掉出所花的时间
-
题解
- 碰撞转向直接看成穿过就好了,因为弹珠之间没有区别
- 以某个向左走于位置i的弹珠为例,没有碰撞时时间为i-0,有碰撞需要记录在i左边且方向向右的弹珠数r,时间为i-0+r,从左往右边遍历遍记录答案和数量即可。某个向右走于位置i的弹珠,n+1-i+l,注意这里是逆序遍历。
-
代码
#include <iostream>
using namespace std;
const int N=1e6+10;
int n,m,d[N],p[N],seg[N];
int main() {
cin>>n>>m;
for(int i=1;i<=m;i++) cin>>d[i];
for(int i=1;i<=m;i++) cin>>p[i],seg[p[i]]=d[i]+1;
int l=0,r=0,ans=0;
for(int i=1;i<=n;i++) {
if(seg[i]==0) continue;
if(seg[i]==1) ans=max(ans,i-0+r);
else r++;
}
for(int i=n;i>=1;i--) {
if(seg[i]==0) continue;
if(seg[i]==2) ans=max(ans,n+1-i+l);
else l++;
}
cout<<ans<<'\n';
return 0;
}
F.困难卷积
-
题意
-
题解
- trick题。如果我们直接暴力枚举两个数组,复杂度为O(n^2)炸了
- 观察数据范围特征,数组的和范围在1e7以内,那么考虑与种类数有关,因为贪心构造一个数种类多的数组为0 1 … x,等差数列求和得到x=sqrt(2e7),即数组中最多有sqrt(2e7)种数,此时遍历的复杂度已经降低了,所以开始考虑如何计算答案
- 以同种类数分别分组a,b后,我们可以得到数num及其出现的次数cnt。那么对于某一组别a1和某一组别b1对于答案的贡献为
(a1.cnt * b1.cnt * floor(sqrt(差))
,那么只需要枚举a分组和b分组,时间复杂度为O(sqrt(2e7)^2)=O(2e7)
-
代码
#include <iostream>
#include <map>
#include <cmath>
using namespace std;
map<int,int> ha,hb;
int sqt(int x,int y) {
return floor(sqrt(abs(x-y)) + 1e-6);//下取整如果是整数计算,加一点精度即可,否则答案不正确
}
int main() {
int n; cin>>n;
for(int i=0;i<n;i++) {
int x; cin>>x;
ha[x]++;
}
for(int i=0;i<n;i++) {
int x; cin>>x;
hb[x]++;
}
long long res=0;
for(auto u:ha)
for(auto v:hb)
res+=1ll*u.second*v.second*sqt(u.first,v.first);
cout<<res<<'\n';
return 0;
}