竞赛讨论区 > 2022长沙学院ACM实验室选拔赛-题解
头像
Bruce12138
发布于 2022-03-15 14:17
+ 关注

2022长沙学院ACM实验室选拔赛-题解

2022长沙学院ACM实验室选拔赛-题解

A.几十里水路到湘江

模拟输出题,可以看到题目给定的约束有一片图像的长宽,河流宽度。

注意到

1.相邻的两片图像会有公共的一行。

2.如果河流宽度超出了图像,那么会适当缩短河流。

实现有很多种方法,可以逐字符输出

#include <bits/stdc++.h>    
using namespace std;
char ans[110][110];
int main()
{
    int n, m, p, q;
    cin >> n >> m >> p >> q;
    int dir = 1, y = 1;
    for (int i = 1; i <= n * q; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
        {
            if (j >= y && j < y + p) cout << '@';
            else cout << '#';
        }
        puts("");
        if (i%n == 0) i ++, dir = -dir;
        y += dir;
    }
    return 0;
}

也可以用存每一行,用存整张图。在蓝桥杯基础技能树题解(一)中,提到过用的方式实现类似二维数组的字符存储。也可以通过样例观察到,令两片图像公共边为第k条边,那么第条和第条是一样的。这时候再循环输出就简单多了。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n,m,p,q,k=0;
    cin>>n>>m>>p>>q;
    vector<string> v;
    for(int i=0;i<n;i++)
    {
        int cnt=0;
        string s="";
        for(int j=0;j<i;j++)
            if(cnt<m)
                cnt++,s+='#';
        for(int j=0;j<p;j++)
            if(cnt<m)
                cnt++,s+='@';
        for(int j=i+p;j<m;j++)
            if(cnt<m)
                cnt++,s+='#';
        s+='\n';
        v.push_back(s);
    }
    cout<<v[0];
    while(q--)
    {
        if(k&1)
            for(int i=v.size()-2;i>=0;i--)
                cout<<v[i];
        else
            for(int i=1;i<v.size();i++)
                cout<<v[i];
        k++;
    }
}

B.帮帮小戴

题目说了一堆废话,其实就是求n个数的最大公约数(greatest common divisor)。

一般手写求GCD用欧几里得算法,也叫辗转相除法。在HDU-LCY算法入门班第2讲《数学基础》中有讲到,可以自己去学一下。

这里给出常用的一个板子:

int gcd(int a,int b){return b?a:gcd(b,a%b);};

在C++的algorithm头文件中,封装了一个GCD函数: ,可以直接得到两个同类型数字的最大公约数。

:同类型指a和b同为等整型,但是不包括等浮点型。

本题直接三个for:第一个for输入n个数,第二个for得到n个数的最大公约数t,第三个for计算

#include<bits/stdc++.h>
using namespace std;
int n,a[1000006],t;
long long ans=0;
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    t=a[0];
    for(int i=1;i<n;i++)
        t=__gcd(t,a[i]);
    for(int i=0;i<n;i++)
        ans+=a[i]/t;
    cout<<ans;
}

C.7的意志

题目对“7的意志”的定义为:

1.正整数
2.是7的倍数

由于,所以n的位数不大于18位,对于所有要删除的可能进行二进制枚举即可。最大需要枚举种可能,即为262144种,评测机可以在1s内完成数据规模2e6的运算~

二进制枚举

对于一个二进制数比如说的二进制是,我们逐位提取二进制位,的视为取,则视为不取。这样就很容易将一个数的各个数位提取出来。

二进制枚举是一个很常用的知识点,在涉及到大型枚举,状压的时候非常好用。相关的知识点还:状态压缩(搜索/dp),bitset

假如给定,当枚举参数是时,取出的数k=135,我们再对135去判断是否满足7的意志即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin>>s;
    int len=s.size();
    int n=1<<len;
    for(int i=1;i<=n;i++)
    {
        int r=i;
        __int128 tmp=0;
        for(int j=0;j<len;j++)
        {
            if(r&1)
                tmp=tmp*10+s[j]-'0';
            r/=10;
        }
        if(tmp%7==0)
        {
            puts("yes");
            return 0;
        }
    }
    puts("no");
    return 0;
}

搜索枚举

对于每一个数位,都有取或者不取两种情况。采用一个dfs去遍历每一个数位考虑取or不取的情况即可。效率与上面的二进制枚举近似一样。只会在数据规模极大或极小的时候有一些内存上的差距,一般不用考虑这方面问题。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int g[25], cnt, ans;
void dfs(int pos, ll x)
{
    if (!pos)
    {
        if (x % 7 == 0 && x) ans ++;
        return;
    }
    dfs(pos - 1, x * 10 + g[pos]);
    dfs(pos - 1, x);
}
int main()
{
    ll n;
    cin >> n;
    while (n) g[++cnt] = n % 10, n /= 10;
    dfs(cnt, 0);
    cout << ans << endl;
    return 0;
}

相关:逐位进行DFS这只是最基础的一个版本,题目还可以变成给定一个整数范围,让你找范围内有多少个数满足要求。那时候就对每个数位进行0~9的枚举,再进行一些其他操作。就会涉及到数位DP,还有一些大型搜索了。

D.qq的考试

晴晴学长说他不写题解,不会的看代码注释,还不会的线下单杀晴晴学长就好。

#include<bits/stdc++.h> 
typedef long long ll;
const ll mod = 1e9 + 7;
using namespace std;
int c[10010];
int main() {
    int n, m;
    cin >> n >> m;
    string a, b;
    cin >> a >> b; //C++的字符串,不会的同学可以用C的char
    int k = 0;
    ll sum = 0; // 注意开long long
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        if (a[i] != b[i]) { //如果答案错误,则把他的分值放入数组c中
            c[++k] = x;
        }
        else { //如果答案正确,则直接加上他的分值。
            sum += x;
        }
    }
    sort(c + 1, c + 1 + k); // 快排,没学的同学使用自己会的排序方法
    if (k < m) { // 如果错误答案数小于最大修改数,则说明所有的错误题目的分值均可加上去
        for (int i = k; i >= 1; i--) {
            sum += c[i];
        }
    }
    else { // 如果错误答案大于等于最大修改数,则把前m大的分值加上去
        for (int i = k; m > 0; m--, i--) {
            sum += c[i];
        }
    }
    cout << sum;
    return 0;
}

P.S 这题还有优先队列解法,不做补充了。

E.RMQ模板

板中板,自己去洛谷学线段树啥的。

这题数据弱了,被一堆O(N*Q)的暴力卡过去了,出题人背锅,很抱歉qwq

静态RMQ问题可以用st表维护效率更高。带修RMQ通常只能用线段树来写,本题STD就是线段树。

冷知识:1e5八仙过海,1e6线段树就随运气了,大概率被卡。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
struct node
{
    int l,r,max;
}t[N<<2];
void build(int k,int l,int r)
{
    t[k].l=l;t[k].r=r;
    if(l==r)
    {
        cin>>t[k].max;
        return ;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    t[k].max=max(t[k<<1].max,t[k<<1|1].max);
}
int query(int k,int l,int r)
{
    if(l<=t[k].l&&t[k].r<=r)
        return t[k].max;
    int ans=-0x3f3f3f3f;
    int mid=(t[k].l+t[k].r)>>1;
    if(l<=mid)ans=max(ans,query(k<<1,l,r));
    if(r>mid)ans=max(ans,query(k<<1|1,l,r));
    return ans;
}
int main()
{
    int n,q,l,r;
    cin>>n>>q;
    build(1,1,n);
    while(q--)
    {
        cin>>l>>r;
        cout<<query(1,l,r)<<endl;
    }
}
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n,q,M[N][20],lo[N];
void ST()
{
    for(int i=2;i<=n;i++) 
        lo[i] = lo[i>>1] + 1;

    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            M[i][j] = max(M[i][j-1],M[i+(1<<(j-1))][j-1]);
        }
    }
}
void quer_x(int l,int r)
{
    int len = r-l+1;
    int k = lo[len];
    int ans = max(M[l][k],M[r-(1<<k)+1][k]);
    printf("%d\n",ans);
}
int main()
{

    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",&M[i][0]);
    ST();
    for(int i=1;i<=q;i++)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        quer_x(l,r);
    }
    return 0;
}

F.哼!想逃?

排序,优先队列,贪心。

给这些人按照啥时候离开排个序就行。能走的就早点走别逗留~

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int t,n,x;
    cin>>t;
    while(t--)
    {
        cin>>n;
        priority_queue<int>q;
        while(n--)
        {
            cin>>x;
            q.push(x);
        }
        while(q.size()&&q.top()>=q.size())
            q.pop();
        cout<<q.size()<<endl;
    }
    return 0;
}

G.马猴烧酒

本题是玉神出的超大型诈骗题,1e1000的数据看上去真的很哈人,但是细心的小朋友就会发现:

我们同时对乘上,得到

如果观察仔细的话就可以发现这里有一个完全平方公式,当且仅当时满足,否则其他时候都是的,所以直接判就行了。

C/C++由于int128的范围也不够放10^1000^,所以用字符串去模拟数字即可。

PYTHON可以直接存进去嗯算,也是无脑过题~

#include<bits/stdc++.h>
#define int long long
using namespace std;
string a,b,c;
signed main()
{
    cin>>a>>b>>c;
    if(a==b&&b==c)cout<<"NO"<<endl;
    else cout<<"YES"<<endl;
}
a,b,c = input().split()
a = int(a)
b = int(b)
c = int(c)

A = a*b//c+b*c//a+c*a//b
B = a+b+c
if(A>B):
    print("YES")
else:
    print("NO")

H.卷王之间的决斗

很不巧,H也是大型诈骗题~
这题如果认真写过上上上周蓝桥训练题欧拉函数的同学,应该问题不大。题面说了一大堆其实就是先计算之间与互质的数的个数,这里的个数就是代表了反对出模板题的人数,则可以得到如果,输出,否则输出。(关于怎么求的个数可以去某度了解一下欧拉函数的定义)

#include<bits/stdc++.h>
using namespace std;
const int N = 30010;


void solve(){
    int arr[N] = {0};
    int n; cin >> n;

    int N = n, ans = n, idx = 0;
    for(int i = 2; i * i <= N; i ++){
        if(n % i == 0) arr[idx++] = i;
        while(n % i == 0) n /= i;
    }
    if(n > 1) arr[idx++] = n;

    for (int i = 0; i < idx; i++){
      ans = ans - ans / arr[i];
    }

    if(ans <= N / 2) cout << "yes" << endl;
    else cout << "no" << endl;

}
int main()
{
    int t; cin >> t;
    for (int i = 0; i < t; i++) solve();
    return 0;
}

全部评论

(1) 回帖
加载中...
话题 回帖

等你来战

查看全部

热门推荐