蓝桥杯周赛第四场
压缩矩阵
题意:将二维矩阵中不为零的数压缩到下标从零开始的一维矩阵中,现在要求给定一维矩阵的索引下表k,求k在二维矩阵中的位置(i,j)
思路:正向推导出结果
如果顺着题目去想k对应的(i,j)是很困难的。所有我们正向求由(i,j)怎么到k。
k表示索引,(i,j)表示所处位置,k就是(i,j)前面不为零的个数,即3(i-1)-1+(j-i)+1=2i+j-3. 因为索引是从0开始的,所以k+1是从(1,1)到(i,j)的个数,因为(i,j)在第i层,所以k+1>=3(i-1)-1+1.即k大于等于3*(i-1)-1.即i小于等于(k+1)/3+1。
代码:
#include <iostream>
#include <math.h>
using namespace std;
typedef long long ll;
int main()
{
// 请在此输入您的代码
ll n,q;
cin>>n>>q;
while(q--){
ll x;
cin>>x;
ll row=floor((x+1)/3+1);
ll col=x-2*row+3;
cout<<row<<" "<<col<<endl;
}
return 0;
}
恒纪元
题意:能够被三个数的n次方和表示的数称之为乱纪元,反之则为恒纪元。现在给定一个乱纪元S,求下一个恒纪元是什么时候,并且恒纪元会持续多长时间。
技巧:对于这种满足条件的数,一般都可以放入stl容器内,使用函数。
思路1:即找到第一个大于S的恒纪元A和第一个大于A的乱纪元D。最后答案为A,D-A。
思路2:找到第一个大于等于S的乱纪元,肯定是其自己,然后将数值连续的跳过,最后到下一个恒纪元arr[l]+1后的乱纪元,即arr[l+1]。
代码1:
#include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
using namespace std;
const __int128 lim = 1e18;
int main() {
ios_base::sync_with_stdio(false);
long long x, y, z;
set<long long> a;
cin >> x >> y >> z;
for (__int128 tx = 1;tx <= lim;tx *= x) {
for (__int128 ty = 1;ty <= lim;ty *= y) {
for (__int128 tz = 1;tz <= lim;tz *= z) {
a.insert(tx + ty + tz);
if (z == 1)break;
}
if (y == 1)break;
}
if (x == 1)break;
}
int q;
cin >> q;
while (q--) {
long long s;
cin >> s;
long long L = s;
while (a.count(L))L++;
long long R = *a.upper_bound(L);
cout << L << ' ' << R - L << endl;
}
return 0;
}
代码2:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e12+7;
int x,y,z,q,s;
set<int> st;
void f()
{
for(int i=0;i<40;i++)
for(int j=0;j<40;j++)
for(int k=0;k<40;k++)
{
int tmp=pow(x,i)+pow(y,j)+pow(z,k);
if(tmp>=N) break;
st.insert(tmp);
}
}
signed main( )
{
cin>>x>>y>>z;
cin>>q;
f();
while(q--)
{
cin>>s;
for(int i=s+1;i<N;i++)
{
if(!st.count(i))
{
set<int>::iterator it;
it=st.find(i-1);
it++;
cout<<i<<' '<<(*it)-i<<endl;
break;
}
}
}
return 0;
}
充能计划
题意:n个飞船引擎需要充能,有m种宝石有si点能量,现在小蓝有q个宝石,其类型位p,充能位置位k(pos),并且同一种类型的宝石只能给一个飞船引擎加一点能量。
思路:排序+区间合并+差分
1.先将同种类型宝石的初始位置放同一个数组,然后排序。
2.将当前区间和前一个区间重叠的部分不要算进去,其他当前区间+1,一段连续的数同时加一个相同的数,我们需要快速反应过来是差分。
3.求前缀和并输出结果。
代码:
非常简单的前缀和
#include <bits/stdc++.h>
using namespace std;
int n,m,q;
int s[100005];
int h[200005];
int dh[200005];
vector <int> G[100005];
int main(){
cin>>n>>m>>q;
for(int i=1;i<=m;i++){
cin>>s[i];
}
while(q--){
int x,pos;
cin>>x>>pos;
G[x].push_back(pos);
}
for(int i=1;i<=m;i++){
sort(G[i].begin(),G[i].end());
}
for(int i=1;i<=m;i++){
int pos=-10000000;
for(int j=0;j<G[i].size();j++){
if(G[i][j]>=pos+s[i]){
dh[G[i][j]]++;
dh[G[i][j]+s[i]]--;
pos=G[i][j];
continue;
}
if(G[i][j]==pos) continue;
dh[pos+s[i]]++;
dh[G[i][j]+s[i]]--;
pos=G[i][j];
}
}
for(int i=1;i<=n;i++){
h[i]=h[i-1]+dh[i];
}
for(int i=1;i<=n;i++){
cout<<h[i]<<' ';
}
return 0;
}
大风起兮
题意:有n个编号的数,每删除一个给定编号的数,求剩下的数从小到大的中位数。
思路:用一个数组存放所有的数,然后开一个vector容器,排序后,每次可以直接删除元素,里面还是有序的。
代码:
#include<bits/stdc++.h>
#define INF 3e9
typedef int ll;
using namespace std;
void solve()
{
ll n;cin>>n;
ll v1[n+1];
vector<ll>v;
for(ll i=1;i<=n;i++){cin>>v1[i];v.push_back(v1[i]);}
v.push_back(-1000);
sort(v.begin(),v.end());
ll q;cin>>q;
while(q--)
{
ll x;cin>>x;
auto pos=lower_bound(v.begin(),v.end(),v1[x]);
v.erase(pos);
if(v.size()%2==0)
{
//11:6
cout<<fixed<<setprecision(1)<<(double)v[v.size()/2]<<" ";
}
else
{
//12:6
cout<<fixed<<setprecision(1)<<((double)v[v.size()/2+1]+v[v.size()/2])/2<<" ";
}
}
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
// ll t;cin>>t;while(t--)
solve();
return 0;
}
总结
这四个题考得都很典型,都需要熟练的使用STL容器解决问题。通过本次题目,对STL容器有了一个全新的认识。
传智杯
删除公共字符
题意:给定两个字符串A,B,将A中所有B的字符删除后输出。
思路:
1.暴力的话需要跑两边for循环
2.可以用map存储,count函数时间复杂度为O(lgn)
代码1:
#include <iostream>
#include <string>
using namespace std;
string a, b;
int main() {
getline(cin, a);
cin >> b;
for (auto &i : a) {
bool flag = false;
for (auto &j : b) {
if (i == j) {
flag = true;
}
}
if (flag == false) cout << i;
}
return 0;
}
代码二:
#include <iostream>
#include <string>
using namespace std;
string a, b;
int main() {
getline(cin, a);
cin >> b;
map<char,int>m;
for(auto i:b)m[i]++;
for(auto i : a) {
if (!m.count(i)) cout << i;
}
return 0;
}
红和紫
题意:
思路:博弈论
如果 n 和 m 都是奇数,那么 n∗m 是奇数,存在一个中心的方格。小红只需要第一步给中间染色,之后紫在任意地方染色,小红只需要在紫染色的方格中心对称的位置染上和紫上次操作相同的颜色即可。这样一定是紫最先无法行动。
如果n 和 m 不全是奇数,说明不存在中心的方格。紫只需要在每次小红染色后,在中心对称的位置上染上和小红上次操作不同的颜色(显然,这样一定是合法的)。如此最后不能操作的一定是小红。
红和蓝
题意:
思路:
一:
二:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
#define x first
#define y second
const int N = 200010;
int n;
int e[N], ne[N], h[N], idx;
int cnt[N], sz[N]; // 子树的点数奇数的数量,以i为根的点数
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
bool dfs(int u, int fa)
{
sz[u] = 1;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == fa) continue;
if(!dfs(j, u)) return false;
sz[u] += sz[j];
if(sz[j] & 1) cnt[u] ++;
}
if(cnt[u] > 1) return false;
return true;
}
int ans[N];
void dfs2(int u, int fa, int c)
{
ans[u] = c;
for(int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if(j == fa) continue;
if(sz[j] & 1) dfs2(j, u, c);
else dfs2(j, u, !c);
}
}
void solve()
{
memset(h, -1, sizeof h);
cin >> n;
for(int i = 0; i < n - 1; i ++)
{
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
if(n & 1)
{
cout << -1 << endl;
return;
}
if(!dfs(1, -1))
{
cout << -1 << endl;
return;
}
dfs2(1, -1, 0);
for(int i = 1; i <= n; i ++)
if(ans[i]) cout << 'B';
else cout << 'R';
cout << endl;
}
int main()
{
/*ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t;
cin >> t;
while(t --)*/
solve();
}