• Codeforces Round #697 (Div. 3) 题解


    Codeforces Round #697 (Div. 3)

    大晚上的,好不容易打个比赛,结果第二天兴致勃勃看分数,发现unrated了,吐血!!!!!!!!!!!

    A. Odd Divisor

    Problem:

      给你一个 n ,问是否有大于 1 的奇数因子

    Solution:

      出了2的次方,其它的都会有奇数因子(自己随便验证一下就好了

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    map<ll,int> mp;
    void init(){
        rep(i,1,55){
            mp[(1ll << i)] ++;
        }
    }
    int main(){
        IOS;
        init();
        int t;
        cin>>t;
        ll n;
        while(t --){
            cin>>n;
            if(mp[n]){
                cout<<"NO"<<endl;
            }else{
                cout<<"YES"<<endl;
            }
        }
        return 0;
    }
    View Code

    B. New Year's Number

    Problem:

      给你一个 n ,问是否能够拆分成 2020 和 2021 的和

    Solution:

      方法一:

        数据比较小,n的最大值才 1e6 ,而满足 2020 * x >= 1e6 的 x 最小值为 1e6/2020 ,所以我们只需要枚举出所有的 2020 * x + 2021 * y就好了

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    map<int,int> mp;
    void init(){
        for(int i = 0;i <= 500;i ++){
            for(int j = 0;j <= 500;j ++){
                mp[i*2020 + j*2021] = 1;
            }
        }    
    }
    int main(){
        IOS;
        init();
        int t;
        cin>>t;
        ll n;
        while(t--){
            cin>>n;
            if(mp[n] == 1){
                cout<<"YES"<<endl;
            }else{
                cout<<"NO"<<endl;
            }
        }
        return 0;
    }
    View Code

      方法二:

        我们从题目中可以得出 2020*x + 2021*y = n --> 2020*x + (2020 + 1)*y = n --> 2020*(x + y) + y = n。

        由上面式子我们可以知道(x + y)是 n 中有几个 2020 ,y 是 n 中除去所有2020 剩下的值,即 (x + y) = n / 2020 ,y = n%2020,有了上面的两个式子,我们就可以求出 x 和 y 的值了,只要 x >= 0 && y >= 0即是 YES 反之是 NO

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e5 + 10;
    int main(){
        IOS;
        int t;
        cin>>t;
        ll n;
        while(t--){
            cin>>n;
            ll y = n % 2020,x = n/2020 - y;
            if(x >= 0){
                cout<<"YES"<<endl;
            }else{
                cout<<"NO"<<endl;
            }
        }
        return 0;
    }
    View Code

    C. Ball in Berland

    Problem:

      a 个男的,b 个女的,有 k 个男女组合(不会出现两个重复的组合)。从 k 个组合中选取 2 个组合,问有多少种选取方式,可以让选出的两个组合中的人不会同时出现在两个组合中。

      例如a=3,b=4,k=4,(1,2),(1,3),(2,2),(3,4)。我们可以选择(1,3)和(3,4),但是不能选择(1,2)和(2,2),因为2这个女的同时出现在两个组合中了。

    Solution:

      其实就是对于每个组合 (a,b),计算出能够有多少个组合中不会出现男 a 和 女 b ,对于k个组合,我们计算出男生 a 出现过几次,女生 b 出现过几次,

      然后用 k - 男生 a 出现过次数 -女生 b 出现过次数 + 1(由于不会出现两个重复的组合,所以男 a 和 女 b 在同一个组合的数量只会为 1,所以只会多减一次)。

      (最后答案要除以二)

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    struct Node{
        int a,b;
        Node(){}
        Node(int _a,int _b):a(_a),b(_b){}
    }nd[maxn];
    int ASum[maxn],BSum[maxn];
    int main(){
        IOS;
        int t,a,b,k;
        cin>>t;
        while(t--){
            cin>>a>>b>>k;
            rep(i,1,k){
                cin>>nd[i].a;
                ASum[nd[i].a] = 0;
            }
            rep(i,1,k){
                cin>>nd[i].b;
                BSum[nd[i].b] = 0;
            }
            rep(i,1,k){
                ASum[nd[i].a] ++;
                BSum[nd[i].b] ++;
            }
            ll ans = 0;
            rep(i,1,k){
                ll res = k - (ASum[nd[i].a] + BSum[nd[i].b] - 1);
                if(res > 0){
                    ans += res;    
                }
            }
            cout<<(ans)/2<<endl;
        }
        return 0;
    }
    View Code

    D. Cleaning the Phone

    Problem:

      有 n 个应用,每个应用占用 ai 大小的空间和由 bi 的方便点(bi 的值为1 或 2),问清除至少 m 大小的空间,最少会花费几个方便点。(即在 n 个应用中,如何删除一些任务,让空间大小大于等于m,并且方便点的和最小)

    Solution:

      由于 bi 为 1 或 2 ,所以我们可以在它上面进行操作。

      设选取 x 个 1 方便点的应用,y 个 2 方便点的应用,如何让x + y*2最小?

      我们能够知道,对于同样的方便点的应用,选取空间最大的一定是最好的,所以我们将 1 方便点和 2 方便点的应用进行按空间大小从小到大进行排序,然后我们可以假设我们选取了 y 个 2 方便点的应用,并且空间和为 sum,那么对于 1 方便点的应用我们就需要选取空间和大于等于 m - sum 空间大小,假设选取了x个,在这个过程中,我们不断的跟新x + y*2的最小值,最终就可以获得答案。

      由于如果我们直接用两个for循环遍历寻找答案的话,时间复杂度会达到O(n*n),所以我们必须进行优化,在寻找m - sum空间的时候,其实我们就是找第一个空间前缀和大于等于m - sum的下标,所以在这我们可以进行二分(直接用lower_bound)。注意,找不到m - sum空间大小的时候,就需要跳过。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    ll t,n,m;
    int a[maxn],b[maxn];
    vector<ll> one,two;
    bool cmp(ll number1,ll number2){
        return number1 > number2;
    }
    int main(){
        IOS;
        cin>>t;
        while(t--){
            one.clear(),two.clear();
            cin>>n>>m;
            rep(i,1,n){
                cin>>a[i];
            }
            rep(i,1,n){
                cin>>b[i];
            }
            rep(i,1,n){
                if(b[i] == 1){
                    one.push_back(a[i]);
                }else{
                    two.push_back(a[i]);
                }
            }
            sort(one.begin(),one.end(),cmp);
            sort(two.begin(),two.end(),cmp);
            _for(i,1,two.size()){
                two[i] += two[i - 1];
            }
            ll ans = INF,sum = 0;
            int j = lower_bound(two.begin(),two.end(),m - sum) - two.begin() + 1;
            if(j <= two.size()) ans = min(ans,j * 2ll);
            _for(i,0,one.size()){
                sum += one[i];
                if(sum < m){
                    j = lower_bound(two.begin(),two.end(),m - sum) - two.begin() + 1;
                    if(j <= two.size()) ans = min(ans,j * 2ll + (i + 1));
                }else{
                    ans = min(ans,i + 1ll);
                }
            }
            if(ans == INF){
                cout<<-1<<endl;    
            }else{
                cout<<ans<<endl;
            }
        }
        return 0;
    }
    View Code

    E. Advertising Agency

    Problem:

      给n 个数的数组a,从中选取 k 个,当选取的 k 个数和为最大值时,问有多少种选法?最终答案对1e9 + 7取模。

      如:n = 4,k = 3,a = {1,3,1,2},则选取 3 个的最大值为 1 + 3 + 2 = 6,而选取方法由a[0] + a[1] + a[3]、a[1] + a[2] + a[3]两种

    Solution:

      找到选取 k 个数和最大时的最小值的数,然后计算 k 个数中需要这个最小值得数几个(假设为m)和这个最小值的数一共有几个(假设为n),最终的答案就是C(n,m),即从 n 个中选取 m 个有几种方法。

      其实就是考个排列组合取模,写上这个模板就好了。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e3 + 10;
    int a[maxn];
    bool cmp(int v1,int v2){
        return v1 > v2;
    }
    const ll mod = 1e9+7;
    ll n,k,ans,inv[maxn],f[maxn];
    ll C(ll x,ll y){
        return f[x] * inv[y] % mod * inv[x - y] % mod;    
    }
    ll A(ll x,ll y){
        return f[x] * inv[x - y] % mod;
    }
    map<int,int> mp;
    int main(){
        IOS;
        inv[0] = f[0] = inv[1] = f[1] = 1;
        _for(i,2,maxn){
            inv[i] = ((mod - mod / i) * inv[mod % i]) % mod;
            f[i] = i;
        }
        _for(i,2,maxn){
            inv[i] = (inv[i] * inv[i - 1]) % mod;
            f[i] = (f[i] * f[i - 1])% mod;
        }
        int t,n,k;
        cin>>t;
        while(t --){
            mp.clear();
            cin>>n>>k;
            rep(i,1,n){
                cin>>a[i];
                mp[a[i]]  ++;
            }
            sort(a + 1,a + 1 + n,cmp);
            int num = 0,sum;
            rep(i,1,k){
                if(a[i] != a[i - 1]){
                    num = 0;
                    sum += a[i];
                }
                num ++;
            }
            cout<<C(mp[a[k]],num)<<endl;
        }
        return 0;
    }
    View Code

    F. Unusual Matrix

    Problem:

      给两个矩阵 a 和 b,其中 a 和 b 矩阵中只会出现 0 和 1,问经过以下操作后能否把 a 矩阵变成 b 矩阵。

      操作:

        (1)将某一行中的 0 变成 1,1 变成 0

        (2)将某一列中的 0 变成 1,1 变成 0

    Solution:

      首先,我们考虑若 a[ i ][ j ] != b[ i ][ j ],那么我们就必须在这个位置上进行一次操作(且只能进行一次操作,大于一次操作都是没有任何意义的),而如果我们对该位置的行进行操作的话,那么在这一行中其实在操作后还有不一样的话,那么只能够通过列进行操作了。由此我们可以知道,其实若 a 矩阵第 i 行和 b 矩阵第 i 行的第一个元素不相等,那么我们一定是需要进行操作的,若相等的话,其实我们这一行也没必要再去考虑需不需要进行操作了,因为这行往后遍历即使有不相等的,然后我们进行操作了,那也会让前面原本相等的变成不相等,这是没有任何意义的。对于列的操作也同理,我们只需要考虑每一列的第一个是否相等即可,不相等则进行操作。在行和列操作之后,我们就可以看 a 矩阵是否和 b 矩阵相等。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 1e3 + 10;
    string a[maxn],b[maxn];
    int main(){
        IOS;
        int t,n;
        cin>>t;
        while(t --){
            cin>>n;
            rep(i,1,n){
                cin>>a[i];
            }
            rep(i,1,n){
                cin>>b[i];
            }
            rep(i,1,n){
                if(a[i][0] != b[i][0]){
                    for(int j = 0;j < n;j ++){
                        a[i][j] = (a[i][j] == '0'?'1':'0');
                    }
                }
            }
            rep(i,0,n){
                if(a[1][i] != b[1][i]){
                    for(int j = 1;j <= n;j ++){
                        a[j][i] = (a[j][i] == '0'?'1':'0');
                    }
                }
            }
            bool ans = true;
            rep(i,0,n){
                if(a[i] != b[i]){
                    ans = false;
                    break;
                }
            }
            cout<<(ans?"YES":"NO")<<endl;
        }
        return 0;
    }
    View Code

    G. Strange Beauty

    Problem:

      给定一个 n 个元素的数组 a,问最少删除 a 数组中的多少个元素可以使得在 a 数组中对于任意的两个元素 a[ i ] 和 a[ j ] (i != j)满足 a[ i ] 可以被 a[ j ] 整除,或者a[ j ] 可以被 a[ i ] 整除

    Solution:

      我们可以设 f[ i ] = x 为当 i 为序列最大的数时最多有x个因子,所以 f[ i ] = 值为  i  的数的个数  + max( f[ j ] ) j 是 i 的因子。

    #include<bits/stdc++.h>
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
    #define _for(i,s,t) for(int i=s;i<t;i++)
    #define _rof(i,s,t) for(int i=s;i>t;i--)
    #define rep(i,s,t) for(int i=s;i<=t;i++)
    #define per(i,s,t) for(int i=s;i>=t;i--)
    #define Ri(x) scanf("%d",&x)
    #define Rii(x,y) scanf("%d%d",&x,&y)
    #define INF 0x3f3f3f3f
    using namespace std;
    template<class T>inline void read(T &res)
    {
        char c;T flag=1;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;res=c-'0';
        while((c=getchar())>='0'&&c<='9')res=res*10+c-'0';res*=flag;
    }
    typedef long long ll;
    const int maxn = 2e5 + 10;
    int a[maxn],cnt[maxn],f[maxn];
    int main(){
        IOS;
        int t,n;
        cin>>t;
        while(t --){
            memset(cnt,0,sizeof(cnt));
            memset(f,0,sizeof(f));
            cin>>n;
            rep(i,1,n){
                cin>>a[i];
                ++cnt[a[i]];
            }
            sort(a + 1,a + n + 1);
            int mxx = 0;
            _for(i,1,maxn){
                f[i] += cnt[i];
                for(int j = i + i;j < maxn;j += i){
                    f[j] = max(f[j],f[i]);
                }
                mxx = max(mxx,f[i]);
            }
            cout<<n - mxx<<endl;
        }
        return 0;
    }
    View Code

    (D、F、G)补题

  • 相关阅读:
    GDOI 2019 退役记
    SHOI2019 游记
    【WC2014】紫荆花之恋
    PKUWC 2019 & NOIWC2019 比赛总结 Formal Version
    WC 2019 颓废记
    VDUVyRLYJC
    Git学习
    DOM学习笔记
    python基础---->AJAX的学习
    python基础---->进程、线程及相关等
  • 原文地址:https://www.cnblogs.com/liuzuolin/p/14336903.html
Copyright © 2020-2023  润新知