首页 > 其他分享 >AtCoder Beginner Contest 172

AtCoder Beginner Contest 172

时间:2023-01-25 13:33:13浏览次数:70  
标签:AtCoder Beginner int cin long tie using 172 dp

A - Calc (abc172 a)

题目大意

给定一个\(a\),输出 \(a + a^2 + a^3\)

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int a;
    cin >> a;
    cout << a + a * a + a * a * a << '\n';

    return 0;
}



B - Minor Change (abc172 b)

题目大意

给定两个字符串\(s\)和 \(t\),问同位置下不同字符的位置数。

解题思路

模拟即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    string s, t;
    cin >> s >> t;
    int ans = 0;
    for(int i = 0; i < s.size(); ++ i)
        ans += (s[i] != t[i]);
    cout << ans << '\n';

    return 0;
}



C - Tsundoku (abc172 c)

题目大意

给定两个数组\(A,B\)和一个数\(s\),从 \(A\)中取 \(x\)个数,从 \(B\)中取 \(y\)个数,要求这 \(x+y\)个数的和不超过\(s\),且 \(x+y\)最大。

解题思路

给两个数组从小到大排个序,然后枚举\(x\),二分找到最大的\(y\),取最大的 \(x+y\)即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n, m;
    LL k;
    cin >> n >> m >> k;
    vector<LL> a(n), b(m);
    for(auto &i : a)
        cin >> i;
    for(auto &i : b)
        cin >> i;
    partial_sum(a.begin(), a.end(), a.begin());
    partial_sum(b.begin(), b.end(), b.begin());
    int ans = upper_bound(b.begin(), b.end(), k) - b.begin();
    for(int i = 0; i < n; ++ i){
        if (a[i] > k)
            break;
        ans = max(ans, i + 1 + int(upper_bound(b.begin(), b.end(), k - a[i]) - b.begin()));
    }
    cout << ans << '\n';

    return 0;
}



D - Sum of Divisors (abc172 d)

题目大意

定义\(f(x)\)为 \(x\)的正因数的个数。

给定 \(n\),求 \(\sum_{k=1}^{n}k \times f(k)\)

解题思路

问题求一个数,所有因数的个数,乘以该数,求和。

转换下求和,考虑因数贡献,即对于一个因数\(x\),对于所有其倍数 \(y\),都会对答案贡献 \(y\)。

因此枚举因数,再枚举其倍数,求其倍数和即可。

时间复杂度为\(O(n\log n)\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    LL ans = 0;
    for(int i = 1; i <= n; ++ i){
        for(int j = i; j <= n; j += i)
            ans += j;
    }
    cout << ans << '\n';

    return 0;
}



E - NEQ (abc172 e)

题目大意

要求统计俩数组对\((A,B)\)的数量,满足长度均为\(n\), \(A,B\)数组中出现的数都在\([1,m]\) 之间,且俩俩互不相同,且对于下标\(i\) ,要求\(A_i \neq B_i\)。

解题思路

对于数组\(A\),有 \(A_{m}^{n}\)种取法,然后考虑数组 \(B\)有多少种取法。

因为数组\(A\)的所有取法都可以视为等价的,因此考虑数组 \(B\)时,可以认为数组 \(A\)为 \(1,2,..,n\)。

此时相当于一个类错排问题,即从 \([1,m]\)中取 \(n\)个数出来,要求第 \(i\)个位置上不能是数字 \(i\)。

对于原错排问题,即从 \([1,i]\)中取 \(i\) 个数出来,要求第 \(j\)个位置上不能是数字 \(j\)。其答案\(dp_i = (i - 1)(dp_{i - 1} + dp_{i - 2}\) 。分别为

  • 考虑原\([1,i-1]\)子问题中的排列,将第 \(i\)个数, 放在第\(i\)位,然后再选择前面的 \(i-1\)中的一个位置交换,得到一个新排列。
  • 考虑原\([1,i-2]\)子问题中的排列,第\(i\)个数放在第\(i\)位,第\(i-1\)个数放在第\(i-1\)位,然后交换这两个数,得到一个新排列。显然这第\(i-1\)个数不一定是第 \(i-1\)个数,它可以是前面任意一个。

在该问题多了\(m-n\)个数,它不受任意条件限制。我们同样考虑递推求\(dp_i\)的值。

  • 如果第 \(i\)个位置放 \(i\),那么和上面的计算方式一样。

  • 如果第 \(i\)个位置放不受限制的数,有 \(m-n\)个数可以选,放了之后,第 \(i\)个数也变成不受限制的数了,此时之后的状态,不受限制的数还是 \(m-n\)个。

因此\(dp_i = (m - n) dp_{i - 1} + (i - 1)(dp_{i - 1} + dp_{i - 2})\)

答案就是\(A_{m}^{n}dp_n\)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int mo = 1e9 + 7;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n, m;
    cin >> n >> m;
    vector<int> dp(n + 1);
    dp[0] = 1;
    dp[1] = m - n;
    for(int i = 2; i <= n; ++ i){
        dp[i] = (1ll * (m - n) * dp[i - 1] % mo + 1ll * (i - 1) * (dp[i - 1] + dp[i - 2]) % mo) % mo;
    }
    int ans = dp[n];
    for(int i = m - n + 1; i <= m; ++ i){
        ans = 1ll * ans * i % mo;
    }
    cout << ans << '\n';

    return 0;
}



F - Unfair Nim (abc172 f)

题目大意

给定\(n\)堆石子,俩人玩 Nim游戏,即每次可从一堆石子取走至少一个石子,不能操作者输。

现在可以从第\(1\)堆拿一些石子放到第\(2\)堆(可以不拿,但不能拿空), 问拿走石子数的最小值,使得后手必胜。或告知不可行。

解题思路

根据\(sg\)理论,全部石子数异或和为 \(0\)则后手必胜。假设前两堆石子数和为\(x\), 后面的石子数异或和为 \(y\) ,则题意转换为

求\(a\)和\(b\),使得 \(a+b = x, a\oplus b = y\) ,且\(a\)不能超过第一堆石子数。最大化\(a\)。

神奇的是 \(a+b = (a\oplus b) + 2(a \& b)\) ,即加法分为了不进位的加法进位两部分。

这样原问题进一步变为\(a \& b = z, a \oplus b = y\),其中 \(z = \frac{x - y}{2}\)。

注意如果\(x-y\)是小于 \(0\)或是 奇数则无解。

此时两个运算都是位运算,我们可以逐位考虑。对于\(z\)和 \(y\)的某一位的情况:

  • 如果是 \(1,1\),则 无解,无法做到\(x \& y = 1, x \oplus y = 1\)。
  • 如果是 \(1,0\),则 \(a,b\)在该位都必须为\(1\)
  • 如果是 \(0,1\),则 \(a,b\)仅有一个在该位为 \(1\)
  • 如果是 \(0,0\),则 \(a,b\)在该位都必须为 \(0\)

因此我们可以构造出不大于第一堆石子数的最大的\(a\)。

第一种情况相当于\(z \& y \neq 0\),此时无解。

由于\(1,0\)的情况两者都必须为\(1\),因此我们可以初始化 \(a\)的值为 \(z\),然后从高位依次判断\(y\)的每一位,看看让\(a\)在该位为 \(1\)会不会大于第一石子数,不大于则可以为\(1\)。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

int main(void) {
    ios::sync_with_stdio(false); 
    cin.tie(0); cout.tie(0);
    int n;
    cin >> n;
    vector<LL> a(n);
    for(auto &i : a){
        cin >> i;
    }
    LL x = a[0] + a[1];
    LL y = 0;
    for(int i = 2; i < n; ++ i)
        y = y ^ a[i];
    auto solve = [&](){
        if ((x - y < 0) || ((x - y) & 1))
            return a[0] + 1;
        x = (x - y) / 2;
        if ((x & y) != 0)
            return a[0] + 1;
        if (x > a[0])
            return a[0] + 1;
        LL ans = x;
        for(int i = 60; i >= 0; -- i){
            if (((y >> i) & 1) && ans + (1ll << i) <= a[0])
                ans += (1ll << i);
        }
        if (ans == 0)
            ans = a[0] + 1;
        return ans;
    };
    LL ans = solve();
    cout << a[0] - ans << '\n';

    return 0;
}



标签:AtCoder,Beginner,int,cin,long,tie,using,172,dp
From: https://www.cnblogs.com/Lanly/p/17066875.html

相关文章

  • 【最短路】Atcoder Beginner Contest 286----E
    题目:E-Souvenir(atcoder.jp)题解:首先这道题可以很容易看出来是求最短路。最开始自己是用bfs写的,出现了WA,TLE,RE等错误。因为对于每种情况会有Q次询问,如果每次询问都......
  • 【多重背包】Atcoder Beginner Contest 286----D
    题目:D-MoneyinHand(atcoder.jp)分析:经典的多重背包。用dp[i]表示i能否正好凑出。先复习一下多重背包。多重背包就是有N组物品,每组最多有k个,每组可以选多个。分组背......
  • CF1726D 题解
    EdgeSplit。一开始nt了,以为红边为一颗树,蓝边为剩余边,蓝边就不会有环了。假设有\(n\)个点,\(m\)条边,且这些边没有出现环,那么连通块的数量为\(n-m\),因为不存在环,......
  • AtCoder Beginner Contest 286 解题报告
    AtCoderBeginnerContest286解题报告\(\text{ByDaiRuiChen007}\)ContestLinkA.RangeSwap直接模拟交换过程即可时间复杂度\(\Theta(n)\)#include<bits/stdc++......
  • AtCoder Beginner Contest 286
    A-RangeSwap(abc286a)题目大意给定长度为\(n\)的数组\(a\)和\(p,q,r,s\),交换\(a[p..q]\)和\(a[r..s]\)并输出交换后的数组\(a\)。解题思路模拟即可。神奇......
  • D - Change Usernames -- ATCODER
    D-ChangeUsernameshttps://atcoder.jp/contests/abc285/tasks/abc285_d 思路DFS深度遍历图。需要注意的是,整个大图中可能有很多小的连通子图,每个子图需要确定起......
  • AtCoder Beginner Contest 043
    A-ChildrenandCandies(ABCEdit)n=int(input())print(n*(n+1)//2)B-UnhappyHacking(ABCEdit)用栈模拟一下?但是栈的遍历比较麻烦这里用vector实现#......
  • AtCoder Beginner Contest 047
    A-FightingoverCandies签到#include<bits/stdc++.h>usingnamespacestd;intread(){...}constintN=1e6+5;intmain(){inta=read(),b=read(......
  • Atcoder ABC 285 题解
    E-WorkorRest题意​ 一周有\(n\)天,给出一个长度为\(n\)的数组\(A\)。你可以决定一周中的休息日与工作日的分布,请问如何选择能够使总贡献最大。​ 如何计算贡献......
  • AtCoder Beginner Contest 285 E(背包dp)
    E-WorkorRest题目大意:给定一周有n天,其中至少有1天为休息日,其余为工作日。同时给定一个长度为n的整型数组A,对于一个工作日,它能产生的工作值为A\(_{min(x,y)}\),其中x......