2023年ACM竞赛班 2023.3.20题解

Posted lxrrrrrrrr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2023年ACM竞赛班 2023.3.20题解相关的知识,希望对你有一定的参考价值。

 目录

瞎编乱造第一题

瞎编乱造第二题

瞎编乱造第三题

瞎编乱造第四题

瞎编乱造第五题

不是很想编了但还是得编的第六题

不是很想编了但还是得编的第七题

还差三道题就编完了的第八题

还差两道题就编完了的第九题

太好啦终于编完了


为啥一周六天早八阿

瞎编乱造第一题

因为偶数因子的操作是可逆,所以我们只要匹配奇数因子就可以了

输入的时候我们就把每个偶数元素一直除到奇数

之后我们枚举b数组每一位右移

匹配上就标记一下

如果全匹配上就是Yes,否则就是No

#include <bits/stdc++.h>
using namespace std;
  
 
const int MAXN = 200005;
int a, bb, b[MAXN];
bool vis[MAXN];
  
void solve()

    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    
        vis[i] = 0;
    
    multiset<int> sa;
    for (int i = 1; i <= n; ++i)
    
        cin >> a;
        while ((a & 1) == 0)
        
            a >>= 1;
        
        sa.insert(a);
    
    for (int i = 1; i <= n; ++i)
    
        cin >> bb;
        while ((bb & 1) == 0)
        
            bb >>= 1;
        
        b[i] = bb;
    
    for (int j = 0; j < 32; ++j)
    
        for (int i = 1; i <= n; ++i)
        
            if (vis[i])
            
                continue;
            
            auto it = sa.find(b[i] >> j);
            if (it != sa.end())
            
                sa.erase(it);
                vis[i] = 1;
            
        
    
    cout << (sa.empty() ? "YES\\n" : "NO\\n");
    return;

signed main()
    int tt;
    cin>>tt;
    while(tt--)
        solve();
    

瞎编乱造第二题

对区间总体加减可以转化为差分问题

第一个操作可以转化为第一个数差分-1,选一个位置差分+1

第二个操作可以转化为选一个位置差分-1

第三个操作可以转化为第一个数差分加1

我们把所有的差分求出来后,先将从2-n所有差分归零,在将第一个数归零即可

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
int n, m, k;
int a[N], d[N];
signed main() 
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin >> T;
    while (T -- ) 
        cin >> n;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        for (int i = 1; i <= n; i ++) d[i] = a[i] - a[i - 1];
        int res = 0;
        for (int i = 2; i <= n; i ++)
            if (d[i] < 0) 
                res -= d[i];
                d[1] += d[i];                
            
            else res += d[i];
        res += abs(d[1]);
        cout << res << '\\n';
    
    return 0;

瞎编乱造第三题

只要从2-n的每个a[i]都是a[1]的倍数即可满足条件

反之则不行

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
signed main() 
    ios::sync_with_stdio(false), cin.tie(0);
    int T;
    int t=1;
    cin>>t;
    while(t--) 
    
        int n;
        cin>>n;
        int a1;
        cin>>a1;
        int flag=1;
        for (int i=2; i<=n; i++) 
        
            int x;cin>>x;
            if (x%a1!=0)flag=0;
        
        if(flag==1) cout<<"Yes";
        else cout<<"No";  
   
        cout << endl;
    
    return 0;

瞎编乱造第四题

当n为奇数时候,先手必胜,因为只要先手第一轮全拿走,第二轮后手就会拿到你的位置,他就输了

当n为偶数时,因为每个人拿的堆是固定的,所以只要看一下谁最少的堆个数少,谁就会先拿完

谁就输了

要是都相等,就是先手输

代码如下

#include <bits/stdc++.h>
  
using i64 = long long;
  
void solve() 
    int n;
    std::cin >> n;
     
    std::vector<int> a(n);
    std::array<i64, 2> step;
    for (int i = 0; i < n; i++) 
        std::cin >> a[i];
    
     
    if (n % 2 == 1) 
        std::cout << "Mike\\n";
        return;
    
     
    for (int t = 0; t < 2; t++) 
        int min = std::numeric_limits<int>::max();
        int j = -1;
        for (int i = t; i < n; i += 2) 
            if (a[i] < min) 
                min = a[i];
                j = i / 2;
            
        
        step[t] = 1LL * (n / 2) * min + j;
    
     
    if (n % 2 == 1 || step[0] > step[1]) 
        std::cout << "Mike\\n";
     else 
        std::cout << "Joe\\n";
    

  
int main() 
    // freopen("9.in","r",stdin);
    // freopen("9.out","w",stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
     
    int t;
    std::cin >> t;
     
    while (t--) 
        solve();
    
     
    return 0;

瞎编乱造第五题

只要这个字符串的最后两位不相等,这个字符串就一定满足条件

所以只要遍历一遍,找两个不相等的字符,贡献就是遍历到他的索引(这个代码里有解释)

之后再加上n(每个单独的字符串都满足条件)

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
signed main() 
    // ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin>>T;
    while(T--) 
    
        long long sum = 0;
        int n;
        scanf("%lld", &n);
        string s = " ";
        cin >> s;
        for(int i = 1; i < n; i++)
            if(s[i] != s[i - 1])
                //+ i的原因在于
                //以i这个位置的字符结尾的子串恰好是这么多
                //↑比如说"1001"的"01",以他们结尾的子串是"1001" "001" "01"正好对应索引3 
                //如果末位2字符成立,所有子串都成立,反之亦然
                //而这里是没有把"1"和"0"算进去的,他们必然是成立的,所以直接在外面加就行了  
                sum += i;
            
        
        printf("%lld\\n", (sum + n));
    
     
    return 0;

不是很想编了但还是得编的第六题

只要找到x的最低位一并保留即可

如果x只有唯一一位1,答案就要再加1(最低位保留一个1,保证异或大于0)

特判一下1

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)
#define INF 0x3f3f3f3f
#define lowbit(x) ((x)&(-x))
const ll MOD = 1000000007;
// const ll MOD = 998244353;
const int N = 500010;
 
void slove()

    int n;
    cin >> n;
    if(n == 1)
        
            cout << 3 << '\\n';
            return;
        
    ll ans = lowbit(n);
    if(ans != n)
        cout << ans << '\\n';
    else
        cout << ans+1 << '\\n';

  
int main(void)

    // freopen("9.in","r",stdin);
    // freopen("9.out","w",stdout);
    IOS;
    int tt = 1;
    cin >> tt;
    while(tt--)
        
            slove();
        
    return 0;

不是很想编了但还是得编的第七题

我们找到一个最先变成奇数的数(如果本来就有奇数,那就不需要这步)

把他变成奇数

之后所有的偶数加上这个奇数变成奇数

代码如下

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
#define lowbit(x) ((x)&(-x))
int a[N];
signed main() 
    // ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin>>T;
    while(T--) 
    
 
        int n;
        cin >> n;
        for(int i=1;i<=n;++i)
            
                cin >> a[i];
            
        int ans = 0;
        for(int i=1;i<=n;++i)
            
                if(a[i]&1)
                    ans++;
            
        if(ans)
            
                cout << n-ans << '\\n';
                continue;
            
        int minn = 1e9+7;
        for(int i=1;i<=n;++i)
            
                int tmp = lowbit(a[i]);
                int cnt = 0;
                while(tmp != 1)
                    
                        tmp/=2;
                        cnt++;
                    
                minn = min(minn , cnt);
            
        cout << minn+n-1 << '\\n';
 
   
    
     
    return 0;

还差三道题就编完了的第八题

就把所有数像山一样排起来就好了,中间大两边小

>2数量的x对答案的贡献是1

(假如这样一个数组,1 1 2 2 3 3 4 4,这样排好之后答案就是4,每个数都有两个以上,每个数都贡献1,排好之后1 2 3 4 4 3 2 1)

其他=1的x对答案贡献是总的这样的数/2上取整

(假如这样一个数组,1 2 3 4 5,排好之后答案是3,因为贡献是5/2上取整,排好之后1 3 5 4 2)

这两组贡献加起来

#include <bits/stdc++.h>
  
using i64 = long long;
  
void solve() 
    int n;
    std::cin >> n;
     
    std::map<int, int> cnt;
    for (int i = 0; i < n; i++) 
        int x;
        std::cin >> x;
        if (cnt[x] < 2) 
            cnt[x]++;
        
    
     
    int ans = 0, v = 0;
    for (auto [x, c] : cnt) 
        if (c == 2) 
            ans++;
         else 
            v++;
        
    
    ans += (v + 1) / 2;
    std::cout << ans << "\\n";

  
int main() 
    // freopen("10.in","r",stdin);
    // freopen("10.out","w",stdout);
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
     
    int t;
    std::cin >> t;
     
    while (t--) 
        solve();
    
     
    return 0;

还差两道题就编完了的第九题

每个陷阱最初的贡献是a[i]-(n-i),先按这个排好序

(躲掉的伤害减去之后增加的伤害)

接下来考虑陷阱之间相互作用

前面每跳过一个陷阱,后面陷阱躲掉的伤害就+1,贡献也+1

所以是排好序的数组遍历前k个

满足a[i]-(n-i)+(前面跳过的陷阱数)>0这个等式,就可以跳

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
#define lowbit(x) ((x)&(-x))
int a[N];
signed main() 
    // ios::sync_with_stdio(false), cin.tie(0);
    int T;
    cin>>T;
    while(T--) 
    
 
        long long sum = 0;
        int n, k;
        cin >> n >> k;
        for (int i = 1; i <= n; ++i)
        
            cin >> a[i];
            sum += a[i];
            a[i] -= n - i;
        
        sort(a + 1, a + 1 + n, greater<long long>());
        for (int i = 1; i <= k; ++i)
        
            if (a[i] > -i)
            
                sum -= a[i] + i - 1;
            
            else
            
                break;
            
        
        cout << sum << '\\n';
 
   
    
     
    return 0;

太好啦终于编完了

对于每个前面是>后面是<的位置,把它称之为山谷

也包括第一个<左边的位置和最后一个>右边的位置

我们让每个山谷都是0

之后向两边扩散,每层加1

每个取max就好

代码如下

#include <bits/stdc++.h>
using namespace std;
string a;
int arr[500002]= 0,;
int main()

    cin>>a;
    int n = a.size();
    for(int i=0;i<n;i++)
    
        if(a[i]== '<')
            arr[i+1] = max(arr[i+1],arr[i]+1);
    
    for(int i=n-1;i>=0;i--)
    
        if(a[i]== '>')
            arr[i] = max(arr[i+1]+1,arr[i]);
    
    long long sum = 0;
    for(int i=0;i<=n;i++)
    
        sum += arr[i];
    
    cout<<sum;

2021年ACM竞赛班训练2021.5.20-问题 E: 调皮的摩尔-题解


Balloon

传送门

时间限制:1秒
空间限制:128M


题目描述

儿童节突然火起来的摩尔庄园放学了,小朋友们需要背上自己的书包回家。小朋友的名字都由长度不超过 200 200 200的英文字母组成,书包编号的范围是 [ 0 , 18446744073709551615 ] [0,18446744073709551615] [0,18446744073709551615]

一位小朋友对应一个书包,对应规则是:

把名字看成是26进制的数,a-z代表0-25。书包编号是十进制的数,且编号有上限,故需对18446744073709551616取模。
提示:小朋友对大小写字母不敏感。

如小朋友Tisfy对应19,8,18,5,24,对应书包编号为 19 ∗ 2 6 4 + 8 ∗ 2 6 3 + 18 ∗ 2 6 2 + 5 ∗ 2 6 1 + 24 ∗ 2 6 0 = 8835474 19*26^4+8*26^3+18*26^2+5*26^1+24*26^0=8835474 19264+8263+18262+5261+24260=8835474
小朋友LetMeFly的书包编号是89816878890
小朋友dongdziz的书包编号是28577520997

题目收录了今日我校CodeForces积分的Top98的id(去除了非字母部分)

小朋友书包
Attcus9018378
BUCTAlex14242578555
BUCTCSNOi370308085272
BUCTChengYijun4399767227251128473
BUCTEnglishYZH4399779354687676265
BUCTWynnLu9628250585870
Bust31583
CandyLynch10964638247999
Coder1162165
DoubleB1102251229
DoubleIce745121835592
Erici2132164
Flos95698
Gallium1858720668
GaoXB2751919
Grendy79136042
HDAnzz84550023
Huangjiajun1096866120774905
Imagining1767073417024
JJcamus2888096970
Jywsuperman1405557075850629
Keenan120720769
LetMeFly89816878890
Lhw7640
MoonDitch2622850920567
NBorz5968195
Nokismet108861147323
PLUTO7062004
Shuigou5652938168
THENuo229024602
Tlopex231029211
TrebleB6073236157
VEGETABLECHICKEN4288866366810972557
Veeee9669612
Wrzcai269599624
XyloseZ7395500969
YeeBy11040482
Yuanshuai5172648573360
Yuzuru294745926
ZZHzzh308599389
bookpen481838721
buctxiong370317506656
ccoke958832
chen40001
codelearner359009297153589
cpz1767
disloss1030227190
dongdziz28577520997
empathy1385107306
f5
fairyjing1046819763840
fangxihao1048233172026
fanle2293958
foreverSeven20421368758857417
fuge101560
gudulangzi36783968039762
happyyasuo38131932775262
hepta3279770
huahua92314404
intmian2634682973
jhas162934
jjli164562
junjie116306688
jxy6706
jywj174989
kagurakanami36740335093058696
kking4751272
knight124899313
kongran3261554673
lhqwd5161159
liangdaye2361509444844
lijiafan90931829729
lisiqi134673144
liuyuanyuan1600649571046253
lixiaolx91098175373
lmy7772
newbiedqt2753690837215
nuchenghao74778368725386
ovo10024
podokoo4801700034
qyn11453
shuqingmei99358535634912
spln326807
takethingseasy10285789247413706668
themostvegetable11925501596008130806
uryuuu245831996
wangqa261623232
windwalker121225299324185
wrzhq10369362
wwwo402130
yjzs428576
yyyyyyyk200473981978
yzzupup7722799619
zero442560
zhaoluo7806318066
zhizhizi203064269338
zouce11684040
zpf17295

以上数据供各位自测。


输入描述

一行一个字符串,代表小朋友的名字,长度不超过200。


输出描述

输出一行一个数,表示这位小朋友的书包编号。


样例一

输入

LetMeFly

输出

89816878890

说明

11 ∗ 2 6 7 + 4 ∗ 2 6 6 + 19 ∗ 2 6 5 + 12 ∗ 2 6 4 + 4 ∗ 2 6 3 + 5 ∗ 2 6 2 + 11 ∗ 2 6 1 + 24 ∗ 2 6 0 = L e t M e F l y 11*26^7+4*26^6+19*26^5+12*26^4+4*26^3+5*26^2+11*26^1+24*26^0=LetMeFly 11267+4266+19265+12264+4263+5262+11261+24260=LetMeFly



提示

摩尔庄园有位小朋友摩尔,非常调皮,喜欢偷偷交换两位小朋友的包。


题目分析

这一题主要是需要对18446744073709551616取模。而18446744073709551616就是以上是关于2023年ACM竞赛班 2023.3.20题解的主要内容,如果未能解决你的问题,请参考以下文章

2021年ACM竞赛班训练2021.5.20-问题 E: 调皮的摩尔-题解

2021年ACM竞赛班训练2021.5.21-问题 A: 尝试看到这道题吧-题解

高精度加法讲解-And-2021年ACM竞赛班训练2021.5.13-问题 F: 最遥远的距离-题解

集合划分讲解-And-2021年ACM竞赛班训练2021.5.20-问题 E: 登上火星-题解

高精度加法讲解-And-2021年ACM竞赛班训练2021.5.13-问题 E: Python大法好-题解

2021年ACM竞赛班训练(十一)