Codeforces Round #697 (Div. 3)(ABCDEFG)

传送门


A . Odd Divisor(思维)

题目大意

给出一个数 n n n,若它含有奇数的质因子那么输出YES,否则输出NO。

解题思路

一开始傻逼了,先是根号 n n n枚举结果TLE了,然后很急,迫不得已上了PR质因数分解的板子。刚交就想到这不就是只有 2 2 2的幂才不会含有奇数质因子吗?我被自己搞傻了。

题解学到一手,用二进制思想判断一个数是否为 2 2 2的幂次,学会了。

//
// Created by Happig on 2021/1/24.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 10;


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    ll n;
    cin >> t;
    while (t--) {
        cin >> n;
        ll ans = n & n - 1;
        if (!ans) cout << "NO" << ENDL;
        else cout << "YES" << ENDL;
    }
    return 0;
}
B. New Year’s Number(暴力)

题目大意

给出一个数 n n n,若它能被若干个2020和2021求和组成,那么输出YES,否则输出NO。

解题思路

因为2020和2021只相差1,那么将 n n n除以2020求出商,然后模2020求出余数,如果余数小于等于商就可以。

//
// Created by Happig on 2021/1/24.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1e5 + 10;


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n;
    cin >> t;
    while (t--) {
        cin >> n;
        int res = n % 2020, k = n / 2020;
        if (res <= k) cout << "YES" << ENDL;
        else cout << "NO" << ENDL;

    }
    return 0;
}
 
C. Ball in Berland(思维+二分图)

题目大意

a a a个男生 b b b个女生,给出 k k k种组合方式,现在需要从中选出两对组合 a 1    b 1 a_1~~b_1 a1  b1 a 2    b 2 a_2~~b_2 a2  b2使得 a 1 ! = a 2 a_1!=a_2 a1!=a2 b 1 ! = b 2 b_1!=b_2 b1!=b2,求出所有可选的方案数。

解题思路

这道题我认为是这一场中最难的一题了(太菜了)。

首先题目中有一个没说的细节,就是不会出现相同的 a i    b i a_i~~b_i ai  bi对。我们对样例一用二分图的方式去思考:

在这里插入图片描述
我们需要考虑每一条边,看哪些是不合法的,显然对于 ( 1 , 2 ) (1,2) (1,2) ( 1 , 3 ) , ( 2 , 2 ) (1,3),(2,2) (1,3),(2,2)是不合法的,观察可以发现不合法的数目实际上和边的端点的度数有关,也就是说边 ( a , b ) (a,b) (a,b)构成的非法组合方式为 d e g ( a ) + d e g ( b ) − 1 deg(a)+deg(b)-1 deg(a)+deg(b)1(减一是因为多加上了原本的边),实际上这个组合也包含了原本的边。那么对于 k k k条边,显然能选的就是 k − ( d e g ( a ) + d e g ( b ) − 1 ) k-(deg(a)+deg(b)-1) k(deg(a)+deg(b)1),累加求和。然后我们发现是大于答案的,再观察发现,因为选择的合法组合两条边都会被计算一次,最后将答案除以2即可。

//
// Created by Happig on 2021/1/24.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

pii g[maxn];
int a[maxn], b[maxn];

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n, m, k;
    cin >> t;
    while (t--) {
        cin >> n >> m >> k;
        memset(a, 0, sizeof a);
        memset(b, 0, sizeof b);
        for (int i = 1; i <= k; i++) cin >> g[i].first;
        for (int i = 1, x, y; i <= k; i++) {
            cin >> g[i].second;
            x = g[i].first, y = g[i].second;
            a[x]++, b[y]++;
        }
        ll ans = 0;
        for (int i = 1, x, y; i <= k; i++) {
            x = g[i].first, y = g[i].second;
            ans += k - a[x] - b[y] + 1;
        }
        cout << ans / 2 << ENDL;
    }
    return 0;
}
D. Cleaning the Phone(前缀和+二分)

题目大意

一个包含 n n n对数的序列,每种数都包含有“重量,价值”两个指标,其中价值要么是 1 1 1要么是 2 2 2,而重量可以达到 1 e 9 1e9 1e9,给出 m m m,问选出多少个数使得至少拿出 m m m重量,且价值最少。

解题思路

第一眼感觉就是01背包,但是重量太大了没法DP。考虑其他做法,将两种价值的数分堆,贪心的想应该降序处理,然后问题就变成了,如何从两个堆中选出两个前缀和,使得满足要求。

一个显然的做法是枚举第一个堆的前缀和然后二分求第二个堆的前缀和使得满足条件,不断更新答案。然而题解是尺取,其实也可以,求两个长度不定的前缀和使得满足一个最值问题,因此可以对两堆同时尺取,一个指针从一堆的头开始,一个指针从另一堆的终点开始,然后根据贪心枚举更新答案即可。

//
// Created by Happig on 2021/1/25.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int num1, num2;
int a[maxn], b[maxn], c[maxn];
ll sum[maxn];


int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n, m;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        num1 = num2 = 0;
        for (int i = 1; i <= n; i++) cin >> c[i];
        for (int i = 1, x; i <= n; i++) {
            cin >> x;
            if (x == 1) a[++num1] = c[i];
            else b[++num2] = c[i];
        }
        sort(a + 1, a + 1 + num1, greater<int>());
        sort(b + 1, b + 1 + num2, greater<int>());
        for (int i = 1; i <= num2; i++) sum[i] = sum[i - 1] + b[i];
        ll ans = inf, cur = 0;
        int pos = lower_bound(sum + 1, sum + 1 + num2, m) - sum;
        if (pos <= num2) ans = min(ans, 2LL * pos);
        for (int i = 1; i <= num1; i++) {
            cur += a[i];
            if (cur < m) {
                pos = lower_bound(sum + 1, sum + 1 + num2, m - cur) - sum;
                if (pos <= num2) ans = min(ans, 2LL * pos + i);
            } else {
                ans = min(ans, 1LL * i);
                break;
            }
        }
        cout << (ans == inf ? -1 : ans) << ENDL;
    }
    return 0;
}
E. Advertising Agency(贪心)

题目大意

给出一个序列,求出有多少种取数的方式使得取出的所有 k k k个数之和是最大值。

解题思路

预处理杨辉三角,先不断地从大到小拿 k k k减去当前最大数的个数,当 k k k小于当前数的个数 x x x时,答案就是 C x k C_x^k Cxk

//
// Created by Happig on 2021/1/25.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1005;

ll C[1005][1005];
int a[maxn];
int num[maxn];
bitset<maxn> vis;
vector<int> res;

void init() {
    C[0][0] = 1;
    for (int i = 1; i <= 1000; i++) {
        C[i][0] = C[i][i] = 1;
        for (int j = 1; j <= (i >> 1); j++) {
            C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % Mod;
        }
    }
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    //ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t, n, m, k;
    init();
    cin >> t;
    while (t--) {
        cin >> n >> k;
        res.clear(), vis.reset();
        for (int i = 1; i <= n; i++) num[i] = 0;
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            if (!num[a[i]]) cnt++;
            num[a[i]]++;
        }
        sort(a + 1, a + 1 + n, greater<int>());
        for (int i = 1; i <= n; i++) {
            if (!vis[a[i]]) {
                vis[a[i]] = 1;
                res.push_back(a[i]);
            }
        }
        int ans = 1;
        for (int i = 0; i < res.size() && k; i++) {
            int cur = res[i];
            if (k >= num[cur]) k -= num[cur];
            else {
                ans = C[num[cur]][k];
                break;
            }
        }
        cout << ans << ENDL;
    }
    return 0;
}
F. Unusual Matrix(思维)

题目大意

给出两个 n ∗ n n*n nn的01矩阵 A , B A,B A,B,对 A A A进行如下两种操作,问能否得到 B B B

  • A A A的某行的所有数都异或 1 1 1(实际上就是1变成0,0变成1)
  • A A A的某列的所有数都异或 1 1 1(实际上就是1变成0,0变成1)

解题思路

挺好的一道思维题。

对于A中某个位置的数,若和B中的数不对应那么必须通过行变换或者列变换将其转化一样。如果转化后该数对应的行或者列有数仍不相同,显然只能在不改变这个数的基础上再进行变换。实际上某行或者某列如果需要变换,那么最多只能操作一次,操作两次是徒劳的。这启发我们,只对第一行所有列进行列变换,然后对第一列所有行进行行变换,最终检验能否变成 B B B

//
// Created by Happig on 2021/1/25.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 1005;

int n;
int a[maxn][maxn], b[maxn][maxn];

bool check() {
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (a[i][j] != b[i][j]) return 0;
        }
    return 1;
}

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    string s;
    cin >> t;
    while (t--) {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> s;
            for (int j = 1; j <= n; j++)
                a[i][j] = s[j - 1] - '0';
        }

        for (int i = 1; i <= n; i++) {
            cin >> s;
            for (int j = 1; j <= n; j++)
                b[i][j] = s[j - 1] - '0';
        }

        for (int j = 1; j <= n; j++) {
            if (a[1][j] != b[1][j]) {
                for (int i = 1; i <= n; i++) {
                    a[i][j] = 1 - a[i][j];
                }
            }
        }
        for (int i = 1; i <= n; i++) {
            if (a[i][1] != b[i][1]) {
                for (int j = 1; j <= n; j++) {
                    a[i][j] = 1 - a[i][j];
                }
            }
        }
        if (check())
            cout << "YES" << ENDL;
        else cout << "NO" << ENDL;
    }
    return 0;
}
G. Strange Beauty(DP)

题目大意

从一个序列中删除若干数使得剩下的数构成的序列任意两数都有倍数关系。

解题思路

这道题和小米邀请赛第一场的A题撞了,实际上就是反向思考,从序列中最多选出多少数使得满足题意。首先记录每个数的种类数然后离散化,那么我们枚举每个数 i i i的所有倍数 j j j,状态转移方程为 d [ j ] = m a x ( d [ j ] , d [ i ] + c n t [ j ] ) d[j] = max(d[j],d[i]+cnt[j]) d[j]=max(d[j],d[i]+cnt[j]),初始所有的 d [ i ] = c n t [ i ] d[i] = cnt[i] d[i]=cnt[i]

//
// Created by Happig on 2021/1/25.
//
#include <bits/stdc++.h>
#include <unordered_map>

using namespace std;
#define ENDL "\n"
#define lowbit(x) (x & (-x))
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int inf = 0x3f3f3f3f;
const double dinf = 1e300;
const ll INF = 1e18;
const int Mod = 1e9 + 7;
const int maxn = 2e5 + 10;

int n;
int a[maxn], cnt[maxn], d[maxn];

int main() {
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        memset(d, 0, sizeof d);
        memset(cnt, 0, sizeof cnt);
        int Max = 0;
        for (int i = 0; i < n; i++) {
            cin >> a[i];
            cnt[a[i]]++;
            Max = max(Max, a[i]);
        }
        sort(a, a + n);
        int m = unique(a, a + n) - a;
        for (int i = 0; i < m; i++) {
            if (!d[a[i]]) d[a[i]] = cnt[a[i]];
            for (int j = 2 * a[i]; j <= Max; j += a[i]) {
                if (cnt[j]) {
                    d[j] = max(d[j], d[a[i]] + cnt[j]);
                }
            }
        }
        int ans = 0;
        for (int i = 0; i < m; i++) {
            ans = max(ans, d[a[i]]);
        }
        ans = n - ans;
        cout << ans << ENDL;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值