Codeforces Round #697 div3 补题

B. New Year's Number

菜到家了,老被hack习惯了

思路

给定一个整数,判断该整数是否由2020和2021组成。比赛时直接枚举,赛后被Hack掉了。实际上,应该将给定的n除以2020,即在n中的2020和2021之和,然后n%2020为2021的个数,因为将2020去掉后,剩下的数就是每个2021多出的1组成的,即多出了多少表示原先有多少个2021。此时判断2020和2021的总和,和2021的个数相比,若是总数大于等于2021的个数,则这个组成是合理的,否则就是不合理的

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    int t;
    cin >> t;
    while(t --){
        int n;
        cin >> n;
        int sum = n / 2020;
        int last = n % 2020;
        if(last <= sum) puts("YES");
        else puts("NO");
    }
    return 0;
}

D. Cleaning the Phone

思路

给定一些应用,这些应用在系统中所占用的内存为ai,而它的重要性为bi,现在要释放一些应用,释放一个应用则释放ai个空间,而丢失bi的重要性,求使得满足至少释放m个空间,最小丢失的重要性之和为多少,其中重要性只会有1和2。考虑枚举拿多少1和2,那么这样的话时间复杂度就一定不够,那么我们可以想到,拿了一定数量的1,那么最少拿多少2是一定的,所以我们可以只枚举拿多少1就行,然后为了尽快的达到m的空间,1和2都应该是拿的可以释放最多空间的那些,所以需要先对1和2分别从大到小排个序,然后求个前缀和方便后面去求,之后枚举1的时候,在2中找到符合要求的个数只需要lower_bound一下就可以了,然后去更新答案即可

代码

#include <bits/stdc++.h>
using namespace std;

#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e5 + 10;

struct Application{
    ll a, b;
}apl[N];

bool cm(ll a , ll b){
    return a > b;
}

int main(){
    fastio;
    int t;
    cin >> t;
    while(t--){
        ll n , m;
        cin >> n >> m;
        vector<ll> va, vb;
        ll sum = 0;
        for(int i = 1;i <= n;i++) cin >> apl[i].a , sum += apl[i].a;
        for(int i = 1;i <= n;i++){
            cin >> apl[i].b;
            if(apl[i].b == 1)
                va.push_back(apl[i].a);
            else
                vb.push_back(apl[i].a);
        }
        if(sum < m){
            cout << -1 << endl;
            continue;
        }
        sort(va.begin(), va.end() , cm);
        sort(vb.begin(), vb.end() , cm);
        int lena = va.size();
        int lenb = vb.size();
        for(int i = 1;i < lena;i++)
            va[i] += va[i - 1] ;
        va.insert(va.begin(), 0);
        for(int i = 1;i < lenb;i++)
            vb[i] += vb[i - 1] ;
        vb.insert(vb.begin(), 0);
        ll res = lena + lenb * 2;
        for(int i = 0;i <= lena;i++){
            ll sum = va[i];
            ll tmp = m - sum;
            int index = lower_bound(vb.begin() , vb.end() , tmp) - vb.begin();
            if(index >= 0 && index <= lenb)
                res = min(res , 1ll * (i + 2 * index));
        }
        cout << res << endl;
    }
    return 0;
}

E. Advertising Agency

思路

给定n个数,选择k个数,求选取最大值的方案有几种。肯定是从大到小贪心的取,那么对于一个数的选取,若是这个数的个数小于剩余要选的数的个数,那么这个大小的数一定是都要选进去的,就是一种方案,而对于数的个数大于剩余要选数的个数的,直接组合数,在数的个数中选剩余要选个数的数。大小是1000,直接数组保存就可以,每组数据都要对数组进行初始化

代码

#include <bits/stdc++.h>
using namespace std;

#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e3 + 10;

ll a[N];
ll C[1005][1005];
ll mod = 1e9 + 7;

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

int main(){
    fastio;
    int t;
    init();
    cin >> t;
    while(t--){
        ll n , k;
        memset(a, 0, sizeof a);
        cin >> n >> k;
        for(int i = 1;i <= n;i++) {
            int x;
            cin >> x;
            a[x] ++;
        }
        bool flag = true;
        for(int i = 1000;i >= 1 && flag; i--){
            if(a[i] >= k)
                cout << C[a[i]][k] << endl , flag = false;
            else
                k -= a[i];
        }
    }
    return 0;
}

F. Unusual Matrix

思路

给定两个矩阵,矩阵元素只有0和1,可以选择两种操作,1——将一行内容翻转,即0变1或1变0,2——将一列内容翻转。问是否可以从第一个矩阵翻转到第二个矩阵。若是两个矩阵相同,则相对应元素异或后应该会得到一个元素全为0的矩阵,那么我们首先可以把两个矩阵相应元素异或。然后我们可以发现对原本第一个矩阵进行操作可以间接的对异或后矩阵进行操作。问题就变成了,是否可以将异或后矩阵翻转为一个全0矩阵。我们可以先将第一列固定为0,然后就不能进行行操作,否则会改变第一列的内容,那么剩下的列都应该要一列中都是相同元素才能将其全部翻转为0,只需要判断剩下的每一列中的元素都和这一列第一行的元素相同

代码

#include <bits/stdc++.h>
using namespace std;

#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 1005;

int a[N][N];

void solve(){
    int n;
    cin >> n;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++){
            char ch;
            cin >> ch;
            a[i][j] = ch - '0';
        }
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++){
            char ch;
            cin >> ch;
            a[i][j] ^= ch - '0';
        }
    for(int i = 1;i <= n;i++)
        if(a[i][1] == 1)
            for(int j = 1;j <= n;j++)
                a[i][j] ^= 1;
    bool flag = true;
    for(int i = 1;i <= n;i++)
        for(int j = 1;j <= n;j++)
            if(a[i][j] != a[1][j])
                flag = 0;
    if(flag) puts("YES");
    else puts("NO");
}

int main(){
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}

G. Strange Beauty

思路

给定一个序列,删除其中的一些,使得每个数都满足一个数是另一个数的倍数,求最少删除多少个数。可以看成是一个从小到大的序列,从中选取满足当前数是前面所有数的倍数,且当前数是其后面所有数的约数,然后需要删除的数,就是所有数的长度减去这个最大的长度。很显然有个O(\(n^2\))的做法,采用类似最长上升子序列的方式,而这样做显然会收获一个TLE。
正确的做法是,采取埃筛的思想,从小到大的枚举每个数,然后当前数的长度就是前面数给当前数贡献的最大长度加上当前数在序列中的个数,然后再去处理当前数给当前数的倍数做出的贡献,即直接枚举两倍的当前数到最大值区间内当前数的倍数(即代码中的 dp[j] = max(dp[j] , dp[i]) 求出 j 的约数,对 j 做出的最大贡献),然后对于每个数都更新一下答案。

代码

#include <bits/stdc++.h>
using namespace std;

#define fastio ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
typedef long long ll;
const int N = 2e5 + 10;

int a[N];
int dp[N];
int cnt[N];

void solve(){
    int n;
    memset(dp , 0 , sizeof dp);
    memset(cnt , 0 , sizeof cnt);
    cin >> n;
    for(int i = 1;i <= n;i++) cin >> a[i] , cnt[a[i]] ++;
    sort(a + 1, a + 1 + n);
    int ans = 0;
    for(int i = 1;i <= a[n];i++){
        dp[i] += cnt[i];
        for(int j = i * 2;j <= a[n];j += i)
            dp[j] = max(dp[j] , dp[i]);
        ans = max(dp[i] , ans);
    }
    cout << n - ans<< endl;
}

int main(){
    fastio;
    int t;
    cin >> t;
    while(t--)
        solve();
    return 0;
}
posted @ 2021-01-27 19:02  zengbiaojie  阅读(117)  评论(0编辑  收藏  举报