文章目录
B 正则表达式
x.x.x.x,其中 x表示一个十进制数字, … 没有特殊含义,表示其本身
给出几个这样的序列,x>=0&&x<=255
求满足条件的有多少序列。
思路
我是把字符转为十进制的数。题解上将数字和 “ . ” 分开输入,倒是没想到,挺简单的,不过我的也不复杂,一个循环而已。
代码
signed main()
{
IOS
int t;
cin>>t;
while(t--)
{
string s;
cin>>s;
int sum=0,f=0;
for(int i=0;i<=s.size();i++)
{
if(s[i]=='.'||i==s.size())
{
if(sum>=0&&sum<=255)
sum=0;
else{
f=1;
sum=0;
}
}
else
sum=sum*10+s[i]-'0';
}
if(f==0)
ans++;
}
cout<<ans<<'\n';
}
C Circle
几个圆相交,最多分成多少区域。
思路
输入
4
0 1 2 3
输出
1 2 4 8
这个是样例,我画了一个四个圆,数的是14,没想那么多,忽然注意到样例,就简单的以为是2的n次方。想着可能我画的图不规范,所以数的不是16。由于前几次萌新的“经验”,果断的提交了,导致了我这场比赛,错的唯一一发。
又仔细看了看图,如果是2^n,又是什么依据呢,怎么会这么大。还是用我的数学判断一下
外部区域 +1
每两个圆应该是都能组成一个新的区域 +n (n-1)
每个圆会有一个公共区域 +1
这样看起来才合理,提交就对了。
因为O很特殊,关键是有半径,自然不可能,想组成一个区域,就组成一个区域,画图也会有偏差,毕竟画的不圆,用数学知识应该能计算出来,题解上的递推,也很合理。当然,这些在我看来只是合理!
代码
void solve()
{
int n;
cin>>n;
int sum=0;
sum=1+n*(n-1)+1;
if(n==0) sum=1;
cout<<sum<<' ';
}
signed main()
{
IOS
int t;
cin>>t;
while(t--)
solve();
}
J keillempkill学姐の卷积
也就是两个矩阵也某种方式(卷积),相乘。
思路
题意很好理解,不过怎么用代码更好的实现,对我还是得想一下。
最初我定义了 i, j,从1到m-n+1循环,这样结果就可以放在C[ i ] [ j ]中,之后再想怎么在这个循环中将两个矩阵相乘,并加到C数组中。根据题意,a数组的每个数都与C数组中的数字有关,所以应该在循环中,再开两个循环,来遍历数组 a,而b数组选择数字的变化,可以用这四个循环来控制b数组的选择。
这样,四个循环就ok了
代码
signed main()
{
IOS
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
cin>>b[i][j];
for(int i=1;i<=m-n+1;i++)
for(int j=1;j<=m-n+1;j++)
{
for(int l=0;l<n;l++)
for(int k=0;k<n;k++)
c[i][j]+=a[l+1][k+1]*b[i+l][j+k];
}
for(int i=1;i<=m-n+1;i++)
{
for(int j=1;j<=m-n+1;j++)
cout<<c[i][j]<<" ";
cout<<'\n';
}
}
L SSH
这是一个模拟题,因为上周也出过。当时一股脑写了100多行,这次动脑子了,写了60多行,没再用结构体,map,vector,实现的。
思路
定义map<string,string> mi 存放私钥和公钥
map<string,map<string,vector > > com; 外层first ,存IPV4地址。内层first,存用户名;second,存公钥
之后进行查询,代码中for,if 套娃,里边放f=1原本的确这样写,麻烦了,一个for就够了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
map<string,string> mi;
map<string,map<string,vector<string> > > com;
signed main()
{
IOS
int m,n,q;
cin>>m>>n>>q;
string pub,pri;
for(int i=1;i<=m;i++)
{
cin>>pub>>pri;
mi[pri]=pub;
}
string ip;
int k;
for(int i=1;i<=n;i++)
{
cin>>ip>>k;
string user,pub2;
int t;
for(int j=1;j<=k;j++)
{
cin>>user>>t;
for(int jj=1;jj<=t;jj++)
{
cin>>pub2;
com[ip][user].push_back(pub2);
}
}
}
string quser,qip,qpri;
while(q--)
{
int f=0;
cin>>quser>>qip>>qpri;
for(auto il:com[qip][quser])
if(il==mi[qpri]) {
f=1;
}
if(f)
cout<<"Yes"<<'\n';
else
cout<<"No"<<'\n';
}
}
F 累加器
给一个数字x,还有Y。x进行y次+1操作。整个过程中x的二进制位数发生了几次变化。
思路
看到这个题,我的第一思路就是第一周的萌新,有一道【D 小蓝的二进制询问】
河南萌新联赛2024第(一)场:河南农业大学_小蓝的二进制询问-CSDN博客
在这个博客中,同样的我们可以选择0做一个参考物。看从0到x会发生几次变化。
0000
0001
0010
0011
0100
0101
0110
可以看出,第一位每个数都在变,第二位每两个数变一次,依次是第N位,每2^(N-1)个数变一次。累加起来就好了。
代码
int jisuan(int x)
{
int e=1,sum=0;
while(x>=e)
{
sum=sum+x/e;
e*=2;
}
return sum;
}
void solve()
{
int x,y;
cin>>x>>y;
int ans=jisuan(y+x)-jisuan(x);
cout<<ans<<'\n';
}
signed main()
{
IOS
int t;
cin>>t;
while(t--)
solve();
}
I 游戏
给定 n 个点, m 条边的有权无向图,每个边有状态,0不可通过。在k节点有钥匙,拿到钥匙,可通过。问从1到n的最小花费。
思路
这是个最短路径问题,用dijkstra算法。需要考虑一个额外条件,是否经过k。
所以用dis存放花费时,dis[ i] [0]表示不经过结点k的花费。dis[i] [1]经过节点k的最小花费
先创建一个邻接表存放图的信息
再创建二维数组存到达某节点的最小花费
用优先队列进行Dijkstra,更新节点最小花费
两种情况取最小值即为答案。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
#define fir(i,a,b) for(int i=a;i<=b;i++)
#define PII pair<int,int>
#define fi first
#define se second
#define tup tuple<int,int,int>//可包含不同类型 int h=get<0>(t)
void solve()
{
int n,m,k,a,b,c,d;
cin>>n>>m>>k;
vector<vector<tup>> g(n+1);//存边的信息
vector<vector<int>> dis(n+1,vector<int>(2,1e11));//定义n+1行两列的数组,初始化1e11
//用于存储从起点到每个节点的最小花费,dis[i][0]表示不经过结点k的花费。dis[i][1]经过节点k的最短时间
priority_queue<tup,vector<tup>,greater<tup> > q;
//用来存储待处理节点,弹出最小花费
fir(i,1,m)
{
cin>>a>>b>>c>>d;
g[a].push_back({b,c,d});
g[b].push_back({a,c,d});
}
q.push({0,1,k==1});
while(!q.empty())
{
auto [distance,place,is_ok]=q.top();
//结构化绑定访问,取出最小花费节点
q.pop();
if(dis[place][is_ok]!=1e11) continue;
//到达该节点的最小花费计算过了,跳过
dis[place][is_ok]=distance;
//更新该节点的最小花费
for(auto [b,c,d]:g[place])
{
int is_now=is_ok==1||b==k;//更新记录有没有经过k
//ok=1,说明经过k,v==k到达钥匙所在节点,判断是否经过K
if(d||is_ok)
q.push({distance+c,b,is_now});
//如果该节点可通过或在此前有钥匙,将新的节点推入
}
}
int ans=min(dis[n][0],dis[n][1]);
if(ans==1e11) cout<<"-1"<<'\n';
else
cout<<ans<<'\n';
}
signed main()
{
IOS
int t;
t=1;
while(t--)
solve();
return 0;
}
E 区间
链接
在一个长度为n的纸带上,初始时所有位置颜色为白色,现在要执行以下两种操作一共q次
操作一:输入一个下标x,你需要将位置x的颜色翻转(白色变为黑色,黑色变为白色)
操作二; 输入两个正整数L, R,你需要输出区间[L,R]中的连续的白色区间长度最大值
思路
看这题的数据,暴力肯定不行。感觉和二分有些关系。(题解上给的线段树,比较复杂,主要是因为我不会😰)。想敲二分没成功,写出来一个纯暴力。
看有人代码用lower_bound() 写的。
定义一个vector,将要翻转的坐标存入,如果存在就删除。这样vector里存放的就是黑块,由于用迭代器查找,插入,所以里面是升序排列。这样当给出L,R.把他们中的黑块处(也就是vector里存放的L,R之间的)计算比较一下就好了。
用set应该也是可以实现的。,原来不行!!!set会超时
vector 是一个动态数组,而set是一个红黑树。
性能差异:set的插入和查找操作的时间复杂度是O(log n),而vector的插入和查找操作的时间复杂度是O(1)。因此,对于大量操作,set可能会比vector慢得多。
排序开销:set内部自动维护元素的有序性,这需要额外的排序开销。对于vector,你可以直接访问任何元素,而不需要进行排序
代码
void solve()
{
int n,q,op,L,R,x;
vector<int> v;
cin>>n>>q;
while(q--)
{
cin>>op;
if(op==1)
{
cin>>x;
auto t=lower_bound(v.begin(),v.end(),x);
if(t!=v.end()&&*t==x)//存在x
v.erase(t);
else
v.insert(t,x);//按升序插入的
}
else
{
cin>>L>>R;
auto t=lower_bound(v.begin(),v.end(),L);
if(t==v.end()||*t>R)
cout<<R-L+1<<'\n';
else
{
int k=*t,ans=*t-L;
t++;
while(t!=v.end()&&*t<=R)
{
ans=max(ans,*t-k-1);
k=*t;
t++;
}
ans=max(ans,R-k);
cout<<ans<<'\n';
}
}
}
}
signed main()
{
IOS
int t;
t=1;
while(t--)
solve();
return 0;
}
D 开心消消乐(Right Version)
思路
完全没想到D竟然如此简单,过的竟然这么少。早知道就应该看看这个题
由于l是递减的,也就是每次只能从后往前消,相同的一起消除,为零的跳过。
代码
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int a[1000500];
signed main()
{
IOS
int n,ans=0;
a[0]=-1;
cin>>n;
for(int i=1;i<=n;i++)
{ cin>>a[i];
if(a[i]!=a[i-1])
{if(a[i]!=0)
ans++;
}
} cout<<ans<<'\n';
}
总结
这次做的挺顺的,不过还是老样子,就会那几道,也就写了那几道。但是D没写出来,真是太可惜了,
虽然现在看,E(存黑块),I (dijkstra),也不复杂,但之前的确没想到,不了解。每一次都像在原地踏步…