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;
}
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值