Codeforces Round #697 (Div. 3)A-G题解

A. Odd Divisor

题目链接:点击此处

每个大于等于2的整数都可以划分为质数的积,然后质数只有2是偶数。所以我们对于一个数除完2,看看是否为1,为1就NO,大于1就是YES。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
ll sum[MAXN];
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        while (n % 2 == 0) {
            n /= 2;
        }
        if (n == 1) { cout << "NO" << endl; }
        else cout << "YES" << endl;
    }

}

B. New Year’s Number

题目链接:点击此处

观察一个数是否被 2020 2020 2020 2021 2021 2021组成,很明显的是 2021 = 2020 + 1 2021=2020+1 2021=2020+1,所以如果这个数 a i a_i ai符合要求,那么 a i = 2020 ∗ x + 2021 ∗ y = 2020 ∗ ( x + y ) + y a_i=2020*x+2021*y=2020*(x+y)+y ai=2020x+2021y=2020(x+y)+y,所以取余2020,会得到 y y y,然后 a i a_i ai减去 2021 ∗ y 2021*y 2021y,观察是否大于等于 0 0 0,并且能被 2020 2020 2020取余,能的话YES,不能NO。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
ll sum[MAXN];
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        ll a = n % 2020;
        ll sum = n - a * 2021;
        if (sum < 0 || sum % 2020 != 0) {
            cout << "NO" << endl;
        }
        else cout << "YES" << endl;
    }

}

C. Ball in Berlan

题目链接:点击此处

给我n个数,那么我们可以得到 ( n − 1 ) ∗ n 2 \frac{(n-1)*n}{2} 2(n1)n组,但是我们要减去不符合的。即出现重复的。这里我们要知道一个性质。如果 a i a_i ai a j a_j aj重复,那么 b i b_i bi b j b_j bj不会重复,因为如果重复,那么就是同一组了,题意不允许出现相同两组。

所以我们遍历数组到 a i a_i ai时,假设前面有 k 1 k_1 k1个与 a i a_i ai重复, k 2 k_2 k2个与 b i b_i bi重复,那么 a n s − = k 1 + k 2 ans-=k_1+k_2 ans=k1+k2。因为这些组不能和 a i , b i {a_i,b_i} ai,bi组一起。对于 a i , b i {a_i,b_i} ai,bi后续的组,如果出现与 a i , b i {a_i,b_i} ai,bi重复,那么通过后续的组来删去。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll a1[MAXN] = { 0 }, b1[MAXN] = { 0 };
ll arr[MAXN]; 
int main() {
    int t;
    cin >> t;
    while (t--) {
        ll k;
        cin >> n >> m >> k;
        ll ans = (k - 1) * k / 2;
        for (int i = 1;i <= k;i++) {
            cin >> a[i];
            ans -= a1[a[i]];
            a1[a[i]]++;
        }
        for (int i = 1;i <= k;i++) {
            cin >> b[i];
            ans -= b1[b[i]];
            b1[b[i]]++;
        }
        for (int i = 1;i <= k;i++) {
            a1[a[i]] = 0;
            b1[b[i]] = 0;
        }
        cout << ans << endl;
        
    }

}

D. Cleaning the Phone

题目链接:点击此处

c 1 [ i ] c1[i] c1[i]为第 i i i大的代价为1的容量。

c 2 [ i ] c2[i] c2[i]为第 i i i大的代价为2的容量。

d 1 [ i ] d1[i] d1[i]表示花费 i i i代价得到的最大容量后,选了几个代价为1的。

d 2 [ i ] d2[i] d2[i]表示花费 i i i代价得到的最大容量后,选了几个代价为2的。

我用的是DP, d p [ i ] dp[i] dp[i]表示花费了 i i i代价,最多能消除多少体积。那么题目转化很简单, d p [ i ] = m a x ( d p [ i − 1 ] + c 1 [ d 1 [ i − 1 ] ] , d p [ i − 2 ] + c 2 [ d 2 [ i − 2 ] ] , d p [ i − 2 ] + c 1 [ d 1 [ i − 2 ] ] + c 1 [ d 1 [ i − 2 ] + 1 ] ) dp[i]=max(dp[i-1]+c1[d1[i-1]],dp[i-2]+c2[d2[i-2]],dp[i-2]+c1[d1[i-2]]+c1[d1[i-2]+1]) dp[i]=max(dp[i1]+c1[d1[i1]],dp[i2]+c2[d2[i2]],dp[i2]+c1[d1[i2]]+c1[d1[i2]+1]),即现在花费 i i i的最大值只能由花费 i − 2 i-2 i2的加上一个代价2的或者两个代价1的,或者花费 i − 1 i-1 i1的加上一个代价1的。之后得到相应最大值后,把 d 1 [ i ] d1[i] d1[i] d 2 [ i ] d2[i] d2[i]随之更新一下就行。

#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 5e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll c1[MAXN], c2[MAXN];
ll dp[MAXN];
ll d1[MAXN], d2[MAXN];
bool cmp(ll& a, ll& b) {
    return a > b;
}
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        int cnt1 = 0, cnt2 = 0;
        int sum = 0;
        for (int i = 1;i <= n;i++)cin >> a[i];
        for (int i = 1;i <= n;i++) { 
            cin >> b[i]; 
            sum += b[i];
            if (b[i] == 1)c1[++cnt1] = a[i];
            else c2[++cnt2] = a[i];
        }
        sort(c1 + 1, c1 + 1 + cnt1,cmp);
        sort(c2 + 1, c2 + 1 + cnt2, cmp);
        d1[0] = d2[0] = 1;
        dp[0] = 0;
        dp[1] = c1[1];
        d1[1] = 2;d2[1] = 1;
        if (dp[1] >= m) { cout << 1 << endl;
        //初始化
        d1[0] = d2[0] = dp[0] = dp[1] = d1[1] = d2[1] = 0;
        for (int i = 1;i <= cnt1;i++)c1[i] = 0;
        for (int i = 1;i <= cnt2;i++)c2[i] = 0;
        continue;
        }
        bool f = false;
        for (int i = 2;i <= sum;i++) {
            int k = 0;
            int tmp = 0;
            if (c1[d1[i - 2]] + c1[d1[i - 2] + 1] > c2[d2[i - 2]]) {
                tmp = c1[d1[i - 2]] + c1[d1[i - 2] + 1];
                dp[i] = dp[i - 2] + tmp;
                d1[i] = d1[i - 2] + 2;
                d2[i] = d2[i - 1];
            }
            else {
                tmp = c2[d2[i - 2]];
                dp[i] = dp[i - 2] + tmp;
                d2[i] = d2[i - 2] + 1;
                d1[i] = d1[i - 2];
            }
            if (tmp + dp[i - 2] < dp[i - 1] + c1[d1[i - 1]]) {
                tmp = c1[d1[i - 1]];
                dp[i] = dp[i - 2] + tmp;
                d1[i] = d1[i - 1] + 1;
                d2[i] = d2[i - 1];
            }
            if (dp[i] >= m) { cout << i << endl;f = true;break; }
           
        }
        //初始化
        for (int i = 1;i <= cnt1;i++)c1[i] = 0;
        for (int i = 1;i <= cnt2;i++)c2[i] = 0;
        for (int i = 0;i <= sum;i++)dp[i] = d1[i] = d2[i] = 0;
        if (!f)cout << -1 << endl;
        
    }

}

E. Advertising Agency

题目链接:点击此处

贪心,所以先对数组排序,肯定要拿前k个。情况很多是因为有很多 a r r [ i ] arr[i] arr[i]可能是等于 a r r [ k ] arr[k] arr[k]的,那么我们定一个 p r e pre pre l a s t last last标记,表示第一个值等于 a r r [ k ] arr[k] arr[k]的下标,和最后一个等于 a r r [ k ] arr[k] arr[k]的下标,然后 l a s t − p r e + 1 last-pre+1 lastpre+1得到 a r r [ k ] arr[k] arr[k]有几个。之后就是组合数+逆元求解。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
bool cmp(ll& a, ll& b) {
    return a > b;
}
void extend_gcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1, y = 0;
        return;
    }
    extend_gcd(b, a % b, x, y);
    ll tmp = x;
    x = y;
    y = tmp - (a / b) * y;
}
//逆元
ll mod_inverse(ll a, ll b) {
    ll x, y;
    extend_gcd(a, b, x, y);
    //防止负数
    return (x % b + b) % b;
}
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 1;i <= n;i++)cin >> arr[i];
        sort(arr + 1, arr + 1 + n,cmp);
        int pre = MAXN;
        int last = 0;
        for (int i = 1;i <= n;i++) {
            if (arr[i] == arr[m]) {
                pre = min(pre, i);
                last = max(last, i);
            }
        }
        ll a = last - pre + 1;
        ll b = m - pre + 1;
        ll ans = 1;
        for (int i = a;i >= b + 1;i--)ans = ans * i % mod;
        for (int i = 1;i <= a - b;i++) {
            ans = ans * mod_inverse(i, mod)%mod;
        }
        cout << ans << endl;
    }

}

F. Unusual Matrix

题目链接:点击此处

很容易知道一行或者一列异或后,会使得所有值翻转。

一个数翻转2次等于没动。

我们枚举矩阵,当枚举到 i , j i,j i,j时,前面枚举到的点都已经匹配完了。如果在该点需要翻转,那么我们不能列翻转,因为翻转列,会使得该列上面几行的点匹配不成功,所以我们只能翻转行,但是翻转行又有问题了,因为该行前面的点已经匹配完了。翻转行会使得前面的点不匹配。有人说,那么在下一行我们翻转列就行,但是我们这样会使得该列最上面那些匹配好的点不匹配,形成矛盾。

所以我们了解到,在一定程度后,我们就不能再翻转了,如果出现没匹配的,那么答案肯定是匹配不了的。

那么我们要知道什么条件才不能翻转。

那就是出现后效性,即我们翻转这个会使得我们前面匹配完的还要翻转就不行。

那么哪些点没有后效性呢?就是第一列的所有点的行翻转无后效性。第一行的所有点的列翻转无后效性。

所以答案很显然了,我们对第一行和第一列进行行列翻转让他们满足要求,之后遍历其他点,如果出现不符合的就是错的。

代码中 m p [ i ] [ j ] = = 1 mp[i][j]==1 mp[i][j]==1表示要翻转的,为0表示不用翻转, h o r [ j ] hor[j] hor[j] v e r [ i ] ver[i] ver[i]表示第j行,第 i i i列是否翻转过。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int mp[MAXN][MAXN];
int ver[MAXN];
int hor[MAXN];
int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 1;i <= n;i++) {
            for (int j = 1;j <= n;j++) {
                char ch;
                cin >> ch;
                mp[i][j] = ch - '0';
            }
        }
        for (int i = 1;i <= n;i++) {
            for (int j = 1;j <= n;j++) {
                char ch;
                cin >> ch;
                mp[i][j] = (mp[i][j] + (ch - '0')) % 2;
            }
        }
        if (n == 1) {
            cout << "YES" << endl;
            continue;
        }
        bool f = true;
        for (int i = 1;i <= n;i++) {
            if (mp[1][i] == 1) {
                hor[i] = 1;
            }
        }
        for (int i = 2;i <= n;i++) {
            if (hor[1])mp[i][1] = mp[i][1] ^ 1;
            if (mp[i][1] == 1)ver[i] = 1;
            else ver[i] = 0;
            for (int j = 2;j <= n;j++) {
                for (int k = 1;k <= ver[i] + hor[j];k++) {
                    mp[i][j] = mp[i][j] ^ 1;
                }
                if (mp[i][j] == 1) { f = false;break; }
            }
            if (!f)break;
        }
        for (int i = 1;i <= n;i++) {
            ver[i] = hor[i] = 0;
        }
        if (f)cout << "YES" << endl;
        else cout << "NO" << endl;
    }

}

G. Strange Beauty

题目链接:点击此处

这题我们要知道如果 a a a能被 b b b整除, b b b能被 c c c整除,那么 a a a能被 c c c整除。

那么我们很容易想到递推。

d p [ b ] + = d p [ a ] dp[b]+=dp[a] dp[b]+=dp[a], d p [ c ] + = d p [ b ] dp[c]+=dp[b] dp[c]+=dp[b]

之后我们想枚举。发现枚举肯定会超时。

所以需要优化。 那么怎么才能知道一个数的整数倍有哪些呢?

我们知道一个数是由质数够成的,所以我们只要枚举质数就行。

所以 d p [ i ] = d p [ i ] + m a x ( d p [ i ∗ p r i m e [ 1 ] ] , d p [ i ∗ p r i m e [ 2 ] ] , . . . d p [ i ∗ p r i m e [ c n t ] ] ) dp[i]=dp[i]+max(dp[i*prime[1]],dp[i*prime[2]],...dp[i*prime[cnt]]) dp[i]=dp[i]+max(dp[iprime[1]],dp[iprime[2]],...dp[iprime[cnt]])

通过这个我们知道我们要从后往前枚举。其中 i i i就表示一个数 i i i,不是表示 a r r [ i ] arr[i] arr[i],如果表示 a r r [ i ] arr[i] arr[i]就会错误,比如样例 2 2 8,我们得到8的 d p dp dp值,但是不能通过 p r i m e prime prime得到2的dp值。只能通过4来作为间接值来得到,即 d p [ 4 ] = d p [ 4 ] + m a x ( d p [ 4 ∗ 2 ] , d p [ 4 ∗ 3 ] . . . ) , d p [ 2 ] = d p [ 2 ] + m a x ( d p [ 2 ∗ 2 ] , d p [ 2 ∗ 3 ] . . . ) dp[4]=dp[4]+max(dp[4*2],dp[4*3]...),dp[2]=dp[2]+max(dp[2*2],dp[2*3]...) dp[4]=dp[4]+max(dp[42],dp[43]...),dp[2]=dp[2]+max(dp[22],dp[23]...)

但是这样枚举肯定复杂度很大,所以要优化,那么就是 i ∗ p r i m e [ j ] i*prime[j] iprime[j]大于2e5时,我们就 b r e a k break break,这样,我们会减少很多复杂度。

这边我们初始化 d p [ i ] dp[i] dp[i]表示在 a r r arr arr中的出现次数。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int dp[MAXN];
int arr[MAXN];
bool visit[MAXN] = { 0 };
int prime[MAXN];
int cnt = 0;
//欧拉筛
void init() {
    for (int i = 2;i < MAXN;i++) {
        if (!visit[i])
            prime[++cnt] = i;
        for (int j = 1;j <= cnt, prime[j] * i < MAXN;j++) {
            visit[prime[j] * i] = true;
            if (i % prime[j] == 0)break;
        }
    }
}
int main() {
    int t;
    cin >> t;
    init();
    while (t--) {
        int n;
        cin >> n;
        int ans = 1;
        for (int i = 1;i <= n;i++) {
            cin >> arr[i];
            dp[arr[i]]++;
        }
        for (int i = 2e5;i >= 1;i--) {
            int maxn = 0;
            for (int j = 1;j <= cnt;j++) {
                if (i * prime[j] > (int)2e5)break;
                if (dp[i * prime[j]] != 0) {
                    maxn = max(maxn, dp[i * prime[j]]);
                }
            }
            dp[i] += maxn;
            ans = max(dp[i], ans);
        }
        for (int i = 1;i <= 2e5;i++) {
            dp[i] = 0;
        }
        cout << n - ans << endl;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值