SDNU_ACM_ICPC_2021_Winter_Practice_1st [个人赛]

SDNU_ACM_ICPC_2021_Winter_Practice_1st [个人赛]

比赛地址

K - Color the ball

题意:

有n个气球,每次都给定两个整数a,b,给a到b内所有的气球涂一个颜色,问你第m个气球有几个颜色,m属于[1,n],气球开始没有颜色

思路:

很简单的前缀和

```c++
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
using namespace std;
typedef long long ll;
inline int IntRead()
{
    
    
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
    
    
        if(ch == '-') w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    
    
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
int tr[100005], sum[100005], a, b;
int main()
{
    
    

    int n;
    while(cin>>n && n)
    {
    
    
        for(int i = 1; i <= n; i++)
            tr[i] = 0;
        for(int i = 1; i <= n; i++)
        {
    
    
            cin>>a>>b;
            tr[a]++;
            tr[b + 1]--;
        }
        for(int i = 1; i <= n; i++)
        {
    
    
            sum[i] = sum[i - 1] + tr[i];
        }
        cout<<sum[1];
        for(int i = 2; i <=n; i++)
            cout<<' '<<sum[i];
        cout<<endl;
    }
    return 0;
}

H - 最大子矩阵

题意:

给你n乘以 m的整数矩阵,在上面找一个 x乘以 y的子矩阵,使得子矩阵中元素的和最大

思路:

直接利用二维前缀和,然后再通过循环扫一遍不断跟新最大值,即可。我当时wa了三遍,为什么呢?噢!是我把输入m,n,x,y写在了最外面,而不是在循环里面,fuck!但是也就是因为这个不经意的小错误让我对这个题又有了别样的想法,因为这个子矩阵不一定要按着是x行y列来的,可以是y行x列,都是矩阵,没有什么不同的,举个栗子:

3 5 1 3

1 2 3 4 5

1 2 3 4 5

1 2 3 4 5

如果只按照1行3的矩阵来搜,那最大值只是3+4+5=12;然而这个矩阵中最大的子矩阵是5 + 5 + 5 = 15;这就很坑 了,但是很显然出题人没有注意到这点,但是我还是写了的,保险点

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <map>
#include <string>
#include <cstring>
#include <cmath>
#include <stack>
using namespace std;
typedef long long ll;
ll tr[1050][1050], sum[1050][1050];
int n, a, b, c, d;
inline int IntRead()
{
    
    
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
    
    
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    
    
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
int main()
{
    
    

    n = IntRead();
    while(n--)
    {
    
    
        a = IntRead();//就是这些输入一不小心放在了循环的外面,导致我wa了三次,淦
        b = IntRead();
        c = IntRead();
        d = IntRead();
        for(int i = 1; i <= a; i++)
        {
    
    
            for(int j = 1; j <= b; j++)
                cin>>tr[i][j];
        }
        for(int i = 1; i <= a; i++)
        {
    
    
            for(int j = 1; j <= b; j++)
            {
    
    
                sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + tr[i][j];//求的是二维前缀和
            }
        }
        ll maxn = 0;//该maxn是x行y列的最大值
        for(int i = c; i <= a; i++)
        {
    
    
            for(int j = d; j <= b; j++)
            {
    
    
                maxn = max(maxn, sum[i][j] + sum[i - c][j - d] - sum[i - c][j] - sum[i][j - d]);//不断更新最大值
            }
        }
        ll maxn2 = 0;//该maxn2是y行x列的子矩阵的最大值
        for(int i = d; i <= a; i++)
        {
    
    
            for(int j = c; j <= b; j++)
            {
    
    
                maxn2 = max(maxn2, sum[i][j] + sum[i - d][j - c] - sum[i - d][j] - sum[i][j - c]);
            }
        }
        cout<<max(maxn2, maxn)<<endl;//输出最大的那个
    }
    return 0;
}

F - Andy’s First Dictionary

题意:

不断输入一堆字符串,有大写有小写也有非字母,让你将其中的英文单词全部转换成小写并按字典序输出

思路:

因为是不断输入,单词之间用的是空格隔开,所以可以用cin不断输入,不用考虑空格,然后对输入的字符串进行遍历,看这个字符是不是字母,如果是字母就把他变小写,如果不是就把他变空格,后期用stringstream可以消除空格。然后再把改造好的字符串塞进set中去自动重排序

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
typedef long long ll;
string s, temp;
set<string>tr;
stringstream ss;
int main()
{
    
    
    while(cin>>s)
    {
    
    
        for(int i = 0; i < s.size(); i++)
        {
    
    
            if(isalpha(s[i]))
                s[i] = tolower(s[i]);
            else
                s[i] = ' ';
        }
        ss.clear();
        ss<<s;
        while(ss>>temp)
            tr.insert(temp);
    }
    set<string>::iterator it;
    for(it = tr.begin(); it != tr.end(); it++)
        cout<<*it<<endl;
    return 0;
}

A - XORwice

题意:

给你两个数a, b,让你找到一个数x使得a ^ x + b ^ x的值最小,输出这个最小值

思路:

1.找规律,直接输出a^b(我也不懂原理.jpg

#include<bits/stdc++.h>
using namespace std;
int a, b, n;
int main()
{
    
    
    cin>>n;
    while(n--)
    {
    
    
        cin>>a>>b;
        int c = a^b;
        cout<<c<<endl;
    }
    return 0;
}

2.jkgg那里嫖来的很巧妙的位运算的方法

先进行分析,如果a,b的二进制数的同一位都是1,那么x的那一位就是1,这样能保证x^a x^b的值都是0,加起来就是0 ,如果a, b 的二进制数的同一位都是0,那x的那一位就是0,这样也是保证x^a x ^ b的和为0, 如果a b 的同一位不同,则无论x的那一位是多少,x ^ a 与 x ^ b 的和都为1

那么我们如何知道a,b的二进制数的同一位是多少呢?利用&1求和来讨论即可,同为1时,&1得到的是2,而同为0的时候得到的全为0 一个是1一个是0 的时候得到的是1,但是因为如果一个是1一个是0,则x的这一位是多少就没有意义,所以可以简化一下,如下

#include <bits/stdc++.h>
using namespace std;
int t;
int a, b;
int ta, tb;
int temp;
int xx[35];
int ans;
int main()
{
    
    
    scanf("%d", &t);
    while(t--)
    {
    
    
        int x = 0;
        memset(xx, 0, sizeof(xx));
        scanf("%d %d", &a, &b);
        ta = a;//保存一下a和b
        tb = b;
        for(int i = 1; i <= 32; ++i)
        {
    
    
            temp = (1 & a) + (1 & b);
            if(temp == 2)   xx[i] = 1;
            else            xx[i] = 0;//简化在此
            a >>= 1;//右移一位,继续进行判断
            b >>= 1;
        }
        for(int i = 32; i >= 1; --i)//这个地方很巧妙,是从后往前遍历的,当xx[i]还是0的时候,无论怎么左移值都不变,左移就可以相当于二进制最后多了一个0,可以继续进行加xx[i]的操作,实现二进制数的构造
        {
    
    
            x <<= 1;
            x += xx[i];
        }
        cout << x << endl;
        ans = (ta ^ x) + (tb ^ x);
    }
    return 0;
}

X 其实等于(a | b)-(a & b)

/*还有一个函数,能实现十进制转任意进制的的转换,而且是直接转换成字符数组,方便对每一位进行操作*/
itoa(a, c, 16)
/*a就是十进制数字,c是用来接收的字符数组,最后一个参数是几进制
可以用该函数进行二进制的操作
*/

M - The Factor

题意:

给你一堆数,让你求这些所有数的乘积的一个至少有三个因子的因子,也就是找到这n个数的乘积的一个最小的不是素数的因子

思路:

因为数据范围很小,直接暴力求每个数的因子放进数组,(如果最后一个因子不是1,记得也要放进数组),然后用sort排序,然后用tr[0] * tr[1],即得到答案

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
inline int IntRead()
{
    
    
    char ch = getchar();
    int s = 0, w = 1;
    while(ch < '0' || ch > '9')
    {
    
    
        if(ch == '-')
            w = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
    
    
        s = s * 10 + ch - '0',
        ch = getchar();
    }
    return s * w;
}
ll n, m, a, b, tr[100005], len;
int main()
{
    
    
    n = IntRead();
    while(n--)//n个样例
    {
    
    
        len = 0;
        m = IntRead();//m个数
        for(int i = 0; i < m; i++)//循环求每个数的所有因子
        {
    
    
            a = IntRead();
            for(int j = 2; j * j <= a; j++)//暴力求a的因子
            {
    
    
                while(a % j == 0)
                {
    
    
                    a /= j;
                    tr[len++] = j;
                }
            }
            if(a > 1)//当a最后除不尽,是素数的时候,不要忘了放进数组
                tr[len++] = a;
        }
        sort(tr, tr +len);
        if(len < 2)
            cout<<-1<<endl;
        else
            cout<<1ll * tr[0] * tr[1]<<endl;
    }
    return 0;
}

G - Covered Points Count

题意:

给你n对数(a,b),表示区间[a, b]的每个点都被覆盖了一次,要求输出从重叠1次到重叠n次的点的个数,并用空格隔开

思路:

第一想法肯定是用差分前缀和,通过扫一遍数组即可,但是奈何数据太狗,开不了1e18的数组,所以就只有用离散化来求解

还不是太明白为什么是这么做,先献上代码吧,等以后想明白了再回来解释

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <queue>
#include <stdlib.h>
#include <sstream>
#include <map>
#include <set>
using namespace std;
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
typedef long long ll;
map<ll, ll>mp;
ll tr[200005];
ll n;
int main()
{
    
    
    cin>>n;
    for(int i = 0; i < n; i++)
    {
    
    
        ll a, b;
        cin>>a>>b;
        mp[a]++;
        mp[b + 1]--;
    }
    ll l = 0, cnt = 0;
    map<ll,ll>::iterator it;
    for(it = mp.begin(); it != mp.end(); it++)
    {
    
    
        tr[cnt] += (it -> first - l);
        l = it -> first;
        cnt += it -> second;
       // cout<<tr[cnt]<<' '<<l<<' '<<cnt<<endl;
    }
    for(int i = 1; i <= n; i++)
        cout<<tr[i]<<' ';
    cout<<'\n';
    return 0;
}

C - Palindromifier

题意:

输入一个字符串,让你通过最多三十次的操作,使该字符串变成长度不超过1e6 的回文串,操作1:让第二个到第x个字符倒序添加到原字符串的左边,简称L,操作2:让第x个字符到第n-1个字符倒序添加到字符串的右边,简称R

思路:

一看就很复杂对吧,其实这个题代码不过短短几行!

首先看题目中的一句话:

It is guaranteed that under these constraints there always is a solution. Also note you do not have to minimize neither the number of operations applied, nor the length of the resulting string, but they have to fit into the constraints.

翻译过来就是:在这些约数下,总有一个解决方案,你不必最小化应用操作数量和结果字符串的长度,但是他们必须符合条件

(其实就是说,如果有多种情况,只需写出一种情况即可,这就很友好了,因为如果他本来是回文串,你也可以通过操作让她变成其他回文串,也就是说你可以找一个规律,让所有字符串都适合)

然后就可以先求一下字符串的长度n = s.size();

第一步:R n - 1,

这步就是把倒数第二位的字母单独挑出来放在最后

第二步:L n

这步就是把原来字符串的第2位到第n位(n是一直不变的,因为操作是将第2位到第m-1位倒序,这里的m是指总的字符串的长度,而我第一步已经将倒数第二个字符复制到最后面去,所以说,原来的倒数第一已经不是倒数第一,是倒数第二也就是可以复制了,所以是2 到 n,这里的n是原来字符串的长度,可能有点绕口,慢慢琢磨琢磨就能懂)

第三步:L 2

这步就是把最后一位的字母弄到第一位来,这样才能保证是回文串,但是又不能直接将最后一位弄过来,但是我们经过第一步第二步的转换,也就是最后一位是倒数第三位,而倒数第三位经过第二步的操作,变成了第二位,所以我们要把第二位弄到开头就行,正正好符合L的操作,所以是L 2

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    
    
    string s;
    cin>>s;
    int n = s.size();
    cout<<3<<endl;
    cout<<"R"<<' '<<n - 1<<endl;
    cout<<"L"<<' '<<n<<endl;
    cout<<"L"<<' '<<2<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51216553/article/details/112895888